representable 0.0.1.alpha1 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +58 -153
- data/lib/representable.rb +18 -42
- data/lib/representable/bindings/json_bindings.rb +69 -0
- data/lib/representable/bindings/xml_bindings.rb +152 -0
- data/lib/representable/definition.rb +1 -12
- data/lib/representable/json.rb +66 -0
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml.rb +32 -38
- data/representable.gemspec +3 -2
- data/test/bindings_test.rb +110 -0
- data/test/json_test.rb +130 -0
- data/test/{roxml_test.rb → representable_test.rb} +28 -9
- data/test/test_helper.rb +2 -0
- data/test/xml_test.rb +192 -0
- metadata +32 -105
- data/History.txt +0 -354
- data/TODO +0 -37
- data/VERSION +0 -1
- data/examples/amazon.rb +0 -35
- data/examples/current_weather.rb +0 -27
- data/examples/dashed_elements.rb +0 -20
- data/examples/library.rb +0 -40
- data/examples/posts.rb +0 -27
- data/examples/rails.rb +0 -70
- data/examples/twitter.rb +0 -37
- data/examples/xml/active_record.xml +0 -70
- data/examples/xml/amazon.xml +0 -133
- data/examples/xml/current_weather.xml +0 -89
- data/examples/xml/dashed_elements.xml +0 -52
- data/examples/xml/posts.xml +0 -23
- data/examples/xml/twitter.xml +0 -422
- data/lib/representable/references.rb +0 -153
- data/spec/definition_spec.rb +0 -495
- data/spec/examples/active_record_spec.rb +0 -41
- data/spec/examples/amazon_spec.rb +0 -54
- data/spec/examples/current_weather_spec.rb +0 -37
- data/spec/examples/dashed_elements_spec.rb +0 -20
- data/spec/examples/library_spec.rb +0 -46
- data/spec/examples/post_spec.rb +0 -24
- data/spec/examples/twitter_spec.rb +0 -32
- data/spec/roxml_integration_test.rb +0 -289
- data/spec/roxml_spec.rb +0 -372
- data/spec/shared_specs.rb +0 -15
- data/spec/spec_helper.rb +0 -5
- data/spec/support/libxml.rb +0 -3
- data/spec/support/nokogiri.rb +0 -3
- data/spec/xml/array_spec.rb +0 -36
- data/spec/xml/attributes_spec.rb +0 -71
- data/spec/xml/encoding_spec.rb +0 -53
- data/spec/xml/namespace_spec.rb +0 -270
- data/spec/xml/namespaces_spec.rb +0 -67
- data/spec/xml/object_spec.rb +0 -82
- data/spec/xml/parser_spec.rb +0 -21
- data/spec/xml/text_spec.rb +0 -71
- data/test/fixtures/book_malformed.xml +0 -5
- data/test/fixtures/book_pair.xml +0 -8
- data/test/fixtures/book_text_with_attribute.xml +0 -5
- data/test/fixtures/book_valid.xml +0 -5
- data/test/fixtures/book_with_authors.xml +0 -7
- data/test/fixtures/book_with_contributions.xml +0 -9
- data/test/fixtures/book_with_contributors.xml +0 -7
- data/test/fixtures/book_with_contributors_attrs.xml +0 -7
- data/test/fixtures/book_with_default_namespace.xml +0 -9
- data/test/fixtures/book_with_depth.xml +0 -6
- data/test/fixtures/book_with_octal_pages.xml +0 -4
- data/test/fixtures/book_with_publisher.xml +0 -7
- data/test/fixtures/book_with_wrapped_attr.xml +0 -3
- data/test/fixtures/dictionary_of_attr_name_clashes.xml +0 -8
- data/test/fixtures/dictionary_of_attrs.xml +0 -6
- data/test/fixtures/dictionary_of_guarded_names.xml +0 -6
- data/test/fixtures/dictionary_of_mixeds.xml +0 -4
- data/test/fixtures/dictionary_of_name_clashes.xml +0 -10
- data/test/fixtures/dictionary_of_names.xml +0 -4
- data/test/fixtures/dictionary_of_texts.xml +0 -10
- data/test/fixtures/library.xml +0 -30
- data/test/fixtures/library_uppercase.xml +0 -30
- data/test/fixtures/muffins.xml +0 -3
- data/test/fixtures/nameless_ageless_youth.xml +0 -2
- data/test/fixtures/node_with_attr_name_conflicts.xml +0 -1
- data/test/fixtures/node_with_name_conflicts.xml +0 -4
- data/test/fixtures/numerology.xml +0 -4
- data/test/fixtures/person.xml +0 -1
- data/test/fixtures/person_with_guarded_mothers.xml +0 -13
- data/test/fixtures/person_with_mothers.xml +0 -10
- data/test/mocks/dictionaries.rb +0 -57
- data/test/mocks/mocks.rb +0 -279
- data/test/support/fixtures.rb +0 -11
- data/test/unit/definition_test.rb +0 -235
- data/test/unit/deprecations_test.rb +0 -24
- data/test/unit/to_xml_test.rb +0 -81
- data/test/unit/xml_attribute_test.rb +0 -39
- data/test/unit/xml_block_test.rb +0 -81
- data/test/unit/xml_bool_test.rb +0 -122
- data/test/unit/xml_convention_test.rb +0 -150
- data/test/unit/xml_hash_test.rb +0 -115
- data/test/unit/xml_initialize_test.rb +0 -49
- data/test/unit/xml_name_test.rb +0 -141
- data/test/unit/xml_namespace_test.rb +0 -31
- data/test/unit/xml_object_test.rb +0 -206
- data/test/unit/xml_required_test.rb +0 -94
- data/test/unit/xml_text_test.rb +0 -71
- data/website/index.html +0 -98
@@ -43,7 +43,7 @@ module Representable
|
|
43
43
|
elsif opts[:from] == :namespace
|
44
44
|
opts[:from] = '*'
|
45
45
|
@sought_type = :namespace
|
46
|
-
elsif opts[:from].to_s.
|
46
|
+
elsif opts[:from].to_s =~ /^@/ # FIXME: move me to xml.
|
47
47
|
@sought_type = :attr
|
48
48
|
opts[:from].sub!('@', '')
|
49
49
|
end
|
@@ -65,7 +65,6 @@ module Representable
|
|
65
65
|
sought_type.is_a?(Class)
|
66
66
|
end
|
67
67
|
|
68
|
-
|
69
68
|
def name?
|
70
69
|
@name == '*'
|
71
70
|
end
|
@@ -88,16 +87,6 @@ module Representable
|
|
88
87
|
|
89
88
|
value
|
90
89
|
end
|
91
|
-
|
92
|
-
def to_ref
|
93
|
-
case sought_type
|
94
|
-
when :attr then XMLAttributeRef
|
95
|
-
when :text then XMLTextRef
|
96
|
-
when :namespace then XMLNameSpaceRef
|
97
|
-
when Symbol then raise ArgumentError, "Invalid type argument #{sought_type}"
|
98
|
-
else XMLObjectRef
|
99
|
-
end.new(self)
|
100
|
-
end
|
101
90
|
|
102
91
|
private
|
103
92
|
def extract_type(as)
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'representable/bindings/json_bindings'
|
3
|
+
|
4
|
+
module Representable
|
5
|
+
module JSON
|
6
|
+
BINDING_FOR_TYPE = { # TODO: refactor #representable_accessor for better extendability.
|
7
|
+
:text => TextBinding,
|
8
|
+
}
|
9
|
+
def self.binding_for_definition(definition)
|
10
|
+
(BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
base.class_eval do
|
15
|
+
include Representable
|
16
|
+
include InstanceMethods
|
17
|
+
end
|
18
|
+
base.extend ClassMethods # DISCUSS: do that dynamically?
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
# Creates a new Ruby object from XML using mapping information declared in the class.
|
23
|
+
#
|
24
|
+
# Example:
|
25
|
+
# book = Book.from_xml("<book><name>Beyond Java</name></book>")
|
26
|
+
def from_json(data, options={})
|
27
|
+
# DISCUSS: extract #from_json call in Bindings to this place.
|
28
|
+
data = ::JSON[data] if data.is_a?(String) # DISCUSS: #from_json sometimes receives a string (in nestings).
|
29
|
+
data ||= {}
|
30
|
+
|
31
|
+
data = data[representation_name] unless options[:wrap] == false
|
32
|
+
|
33
|
+
create_from_json.tap do |inst|
|
34
|
+
refs = representable_attrs.map {|attr| JSON.binding_for_definition(attr) }
|
35
|
+
|
36
|
+
refs.each do |ref|
|
37
|
+
value = ref.value_in(data)
|
38
|
+
|
39
|
+
inst.send(ref.definition.setter, value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def create_from_json(*args)
|
46
|
+
new(*args)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module InstanceMethods # :nodoc:
|
51
|
+
# Returns a Nokogiri::XML object representing this object.
|
52
|
+
def to_json(options={})
|
53
|
+
attributes = {}.tap do |root|
|
54
|
+
refs = self.class.representable_attrs.map {|attr| JSON.binding_for_definition(attr) }
|
55
|
+
|
56
|
+
refs.each do |ref|
|
57
|
+
value = public_send(ref.accessor) # DISCUSS: eventually move back to Ref.
|
58
|
+
ref.update_json(root, value) if value
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
options[:wrap] == false ? attributes : {self.class.representation_name => attributes}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end # Xml
|
66
|
+
end
|
data/lib/representable/xml.rb
CHANGED
@@ -1,50 +1,44 @@
|
|
1
|
+
require 'representable'
|
2
|
+
require 'representable/bindings/xml_bindings'
|
3
|
+
|
1
4
|
module Representable
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
representable_accessor(*args)
|
5
|
+
module XML
|
6
|
+
BINDING_FOR_TYPE = {
|
7
|
+
:attr => AttributeBinding,
|
8
|
+
:text => TextBinding,
|
9
|
+
:namespace=> NamespaceBinding,
|
10
|
+
}
|
11
|
+
|
12
|
+
def self.binding_for_definition(definition)
|
13
|
+
(BINDING_FOR_TYPE[definition.sought_type] or ObjectBinding).new(definition)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.included(base)
|
17
|
+
base.class_eval do
|
18
|
+
include Representable
|
19
|
+
include InstanceMethods
|
20
|
+
extend ClassMethods
|
19
21
|
end
|
20
|
-
|
22
|
+
end
|
23
|
+
|
24
|
+
class Definition < Representable::Definition
|
25
|
+
# FIXME: extract xml-specific from Definition.
|
21
26
|
end
|
22
27
|
|
23
28
|
module ClassMethods
|
29
|
+
def definition_class
|
30
|
+
Definition
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a new Ruby object from XML using mapping information declared in the class.
|
24
34
|
#
|
25
|
-
#
|
26
|
-
# annotated in the class.
|
27
|
-
#
|
28
|
-
# The input data is either an XML::Node, String, Pathname, or File representing
|
29
|
-
# the XML document.
|
30
|
-
#
|
31
|
-
# Example
|
32
|
-
# book = Book.from_xml(File.read("book.xml"))
|
33
|
-
# or
|
35
|
+
# Example:
|
34
36
|
# book = Book.from_xml("<book><name>Beyond Java</name></book>")
|
35
|
-
#
|
36
|
-
# _initialization_args_ passed into from_xml will be passed into
|
37
|
-
# the object's .new, prior to populating the xml_attrs.
|
38
|
-
#
|
39
|
-
# After the instatiation and xml population
|
40
|
-
#
|
41
|
-
# See also: xml_initialize
|
42
|
-
#
|
43
37
|
def from_xml(data, *args)
|
44
38
|
xml = Nokogiri::XML::Node.from(data)
|
45
39
|
|
46
40
|
create_from_xml(*args).tap do |inst|
|
47
|
-
refs = representable_attrs.map {|attr| attr
|
41
|
+
refs = representable_attrs.map {|attr| XML.binding_for_definition(attr) }
|
48
42
|
|
49
43
|
refs.each do |ref|
|
50
44
|
value = ref.value_in(xml)
|
@@ -61,12 +55,12 @@ module Representable
|
|
61
55
|
end
|
62
56
|
|
63
57
|
module InstanceMethods # :nodoc:
|
64
|
-
# Returns
|
58
|
+
# Returns a Nokogiri::XML object representing this object.
|
65
59
|
def to_xml(params={})
|
66
60
|
params.reverse_merge!(:name => self.class.representation_name)
|
67
61
|
|
68
62
|
Nokogiri::XML::Node.new(params[:name].to_s, Nokogiri::XML::Document.new).tap do |root|
|
69
|
-
refs = self.class.representable_attrs.map {|attr| attr
|
63
|
+
refs = self.class.representable_attrs.map {|attr| XML.binding_for_definition(attr) }
|
70
64
|
|
71
65
|
refs.each do |ref|
|
72
66
|
value = public_send(ref.accessor) # DISCUSS: eventually move back to Ref.
|
data/representable.gemspec
CHANGED
@@ -12,17 +12,18 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.email = ["apotonick@gmail.com"]
|
13
13
|
s.homepage = "http://representable.apotomo.de"
|
14
14
|
s.summary = %q{Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties and compositions.}
|
15
|
-
s.description = %q{
|
15
|
+
s.description = %q{Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties and compositions.}
|
16
16
|
|
17
17
|
s.files = `git ls-files`.split("\n")
|
18
18
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
20
|
s.require_paths = ["lib"]
|
21
21
|
|
22
|
-
s.add_dependency "activesupport"
|
22
|
+
s.add_dependency "activesupport"
|
23
23
|
s.add_dependency "hooks"
|
24
24
|
s.add_dependency "nokogiri"
|
25
25
|
s.add_dependency "i18n"
|
26
|
+
s.add_dependency "json"
|
26
27
|
|
27
28
|
s.add_development_dependency "rspec"
|
28
29
|
s.add_development_dependency "test_xml"
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ReferenceTest < MiniTest::Spec
|
4
|
+
describe "ObjectRef with []" do
|
5
|
+
before do
|
6
|
+
@ref = Representable::XML::ObjectBinding.new(Representable::Definition.new(:songs, :as => [Hash]))
|
7
|
+
end
|
8
|
+
|
9
|
+
it "responds to #default" do
|
10
|
+
assert_equal [], @ref.send(:default)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
describe "TextRef#value_in" do
|
16
|
+
def parse_xml(xml); Nokogiri::XML::Node.from(xml); end
|
17
|
+
|
18
|
+
before do
|
19
|
+
@ref = Representable::XML::TextBinding.new(Representable::Definition.new(:song))
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns found value" do
|
23
|
+
assert_equal "Unkoil", @ref.value_in(parse_xml("<a><song>Unkoil</song></a>"))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class DefinitionTest < MiniTest::Spec
|
29
|
+
describe "generic API" do
|
30
|
+
before do
|
31
|
+
@def = Representable::Definition.new(:songs)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "responds to #typed?" do
|
35
|
+
assert ! @def.typed?
|
36
|
+
assert Representable::Definition.new(:songs, :as => Hash).typed?
|
37
|
+
assert Representable::Definition.new(:songs, :as => [Hash]).typed?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
describe "#apply" do
|
43
|
+
it "works with a single item" do
|
44
|
+
@d = Representable::Definition.new(:song)
|
45
|
+
assert_equal 2, @d.apply(1) { |v| v+1 }
|
46
|
+
end
|
47
|
+
|
48
|
+
it "works with collection" do
|
49
|
+
@d = Representable::Definition.new(:song, :as => [])
|
50
|
+
assert_equal [2,3,4], @d.apply([1,2,3]) { |v| v+1 }
|
51
|
+
end
|
52
|
+
|
53
|
+
it "skips with collection and nil" do
|
54
|
+
@d = Representable::Definition.new(:song, :as => [])
|
55
|
+
assert_equal nil, @d.apply(nil) { |v| v+1 }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe ":as => []" do
|
60
|
+
before do
|
61
|
+
@def = Representable::Definition.new(:songs, :as => [], :tag => :song)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "responds to #accessor" do
|
65
|
+
assert_equal "songs", @def.accessor
|
66
|
+
end
|
67
|
+
|
68
|
+
it "responds to #array?" do
|
69
|
+
assert @def.array?
|
70
|
+
end
|
71
|
+
|
72
|
+
it "responds to #name" do
|
73
|
+
assert_equal "songs", @def.accessor
|
74
|
+
end
|
75
|
+
|
76
|
+
it "responds to #instance_variable_name" do
|
77
|
+
assert_equal :"@songs", @def.instance_variable_name
|
78
|
+
end
|
79
|
+
|
80
|
+
it "responds to #setter" do
|
81
|
+
assert_equal :"songs=", @def.setter
|
82
|
+
end
|
83
|
+
|
84
|
+
it "responds to #sought_type" do
|
85
|
+
assert_equal :text, @def.sought_type
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
describe ":as => [Item]" do
|
91
|
+
before do
|
92
|
+
@def = Representable::Definition.new(:songs, :as => [Hash])
|
93
|
+
end
|
94
|
+
|
95
|
+
it "responds to #sought_type" do
|
96
|
+
assert_equal Hash, @def.sought_type
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
describe ":as => Item" do
|
102
|
+
before do
|
103
|
+
@def = Representable::Definition.new(:songs, :as => Hash)
|
104
|
+
end
|
105
|
+
|
106
|
+
it "responds to #sought_type" do
|
107
|
+
assert_equal Hash, @def.sought_type
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
data/test/json_test.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'representable/json'
|
3
|
+
|
4
|
+
module JsonTest
|
5
|
+
class APITest < MiniTest::Spec
|
6
|
+
Json = Representable::JSON
|
7
|
+
Def = Representable::Definition
|
8
|
+
|
9
|
+
describe "Xml module" do
|
10
|
+
describe "#binding_for_definition" do
|
11
|
+
it "returns ObjectBinding" do
|
12
|
+
assert_kind_of Json::ObjectBinding, Json.binding_for_definition(Def.new(:band, :as => Hash))
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns TextBinding" do
|
16
|
+
assert_kind_of Json::TextBinding, Json.binding_for_definition(Def.new(:band))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class PropertyTest < MiniTest::Spec
|
23
|
+
describe "property :name" do
|
24
|
+
class Band
|
25
|
+
include Representable::JSON
|
26
|
+
representable_property :name
|
27
|
+
end
|
28
|
+
|
29
|
+
it "#from_json creates correct accessors" do
|
30
|
+
band = Band.from_json({:band => {:name => "Bombshell Rocks"}}.to_json)
|
31
|
+
assert_equal "Bombshell Rocks", band.name
|
32
|
+
end
|
33
|
+
|
34
|
+
it "#to_json serializes correctly" do
|
35
|
+
band = Band.new
|
36
|
+
band.name = "Cigar"
|
37
|
+
|
38
|
+
assert_equal "{\"band\"=>{\"name\"=>\"Cigar\"}}", band.to_json.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "property :name, :as => []" do
|
43
|
+
class CD
|
44
|
+
include Representable::JSON
|
45
|
+
representable_property :songs, :as => []
|
46
|
+
end
|
47
|
+
|
48
|
+
it "#from_json creates correct accessors" do
|
49
|
+
cd = CD.from_json({:cd => {:songs => ["Out in the cold", "Microphone"]}}.to_json)
|
50
|
+
assert_equal ["Out in the cold", "Microphone"], cd.songs
|
51
|
+
end
|
52
|
+
|
53
|
+
it "#to_json serializes correctly" do
|
54
|
+
cd = CD.new
|
55
|
+
cd.songs = ["Out in the cold", "Microphone"]
|
56
|
+
|
57
|
+
assert_equal "{\"cd\"=>{\"songs\"=>[\"Out in the cold\", \"Microphone\"]}}", cd.to_json.to_s
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class TypedPropertyTest < MiniTest::Spec
|
63
|
+
describe ":as => Item" do
|
64
|
+
class Label
|
65
|
+
include Representable::JSON
|
66
|
+
representable_property :name
|
67
|
+
end
|
68
|
+
|
69
|
+
class Album
|
70
|
+
include Representable::JSON
|
71
|
+
representable_property :label, :as => Label
|
72
|
+
end
|
73
|
+
|
74
|
+
it "#from_json creates one Item instance" do
|
75
|
+
album = Album.from_json({:album => {:label => "Fat Wreck"}}.to_json)
|
76
|
+
assert_equal "Bad Religion", album.label.name
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "#to_json" do
|
80
|
+
it "serializes" do
|
81
|
+
label = Label.new; label.name = "Fat Wreck"
|
82
|
+
album = Album.new; album.label = label
|
83
|
+
|
84
|
+
assert_equal "{\"album\"=>{\"label\"=>{\"name\"=>\"Fat Wreck\"}}}", album.to_json.to_s
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
class CollectionTest < MiniTest::Spec
|
92
|
+
describe ":as => [Band]" do
|
93
|
+
class Band
|
94
|
+
include Representable::JSON
|
95
|
+
representable_property :name
|
96
|
+
|
97
|
+
def initialize(name="")
|
98
|
+
self.name = name
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class Compilation
|
103
|
+
include Representable::JSON
|
104
|
+
representable_property :bands, :as => [Band]
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "#from_json" do
|
108
|
+
it "pushes collection items to array" do
|
109
|
+
cd = Compilation.from_json({:compilation => {:bands => [
|
110
|
+
{:name => "Cobra Skulls"},
|
111
|
+
{:name => "Diesel Boy"}]}}.to_json)
|
112
|
+
assert_equal ["Cobra Skulls", "Diesel Boy"], cd.bands.map(&:name).sort
|
113
|
+
end
|
114
|
+
|
115
|
+
it "collections can be empty" do
|
116
|
+
cd = Compilation.from_json({:compilation => {}}.to_json)
|
117
|
+
assert_equal [], cd.bands
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it "responds to #to_json" do
|
122
|
+
cd = Compilation.new
|
123
|
+
cd.bands = [Band.new("Diesel Boy"), Band.new("Bad Religion")]
|
124
|
+
|
125
|
+
assert_equal "{\"compilation\"=>{\"bands\"=>[{\"name\"=>\"Diesel Boy\"}, {\"name\"=>\"Bad Religion\"}]}}", cd.to_json.to_s
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|