representable 1.5.0 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.textile +4 -0
- data/README.md +24 -1
- data/TODO +4 -1
- data/lib/representable/binding.rb +0 -4
- data/lib/representable/decorator.rb +1 -0
- data/lib/representable/hash.rb +8 -9
- data/lib/representable/hash_methods.rb +5 -6
- data/lib/representable/json/collection.rb +9 -9
- data/lib/representable/version.rb +1 -1
- data/lib/representable/xml/collection.rb +9 -9
- data/representable.gemspec +2 -2
- data/test/json_test.rb +47 -30
- data/test/representable_test.rb +6 -0
- data/test/test_helper.rb +0 -2
- data/test/xml_test.rb +119 -94
- metadata +8 -8
data/CHANGES.textile
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
h2. 1.5.1
|
2
|
+
|
3
|
+
* Make lonely collections and hashes work with decorators.
|
4
|
+
|
1
5
|
h2. 1.5.0
|
2
6
|
|
3
7
|
* All lambdas now receive user options, too. Note that this might break your existing lambdas (especially with `:extend` or `:class`) raising an `ArgumentError: wrong number of arguments (2 for 1)`. Fix this by declaring your block params correctly, e.g. `lambda { |name, *|`. Internally, this happens by running all lambdas through the new `Binding#represented_exec_for`.
|
data/README.md
CHANGED
@@ -64,7 +64,7 @@ song = Song.new.extend(SongRepresenter).from_json(%{ {"title":"Roxanne"} })
|
|
64
64
|
|
65
65
|
## Extend vs. Decorator
|
66
66
|
|
67
|
-
If you don't want representer modules to be mixed into your objects (using `#extend`) you can use the `Decorator` strategy [described below](
|
67
|
+
If you don't want representer modules to be mixed into your objects (using `#extend`) you can use the `Decorator` strategy [described below](#decorator-vs-extend). Decorating instead of extending was introduced in 1.4.
|
68
68
|
|
69
69
|
|
70
70
|
## Aliasing
|
@@ -205,6 +205,29 @@ class AlbumRepresentation < Representable::Decorator
|
|
205
205
|
end
|
206
206
|
```
|
207
207
|
|
208
|
+
### Helpers In Decorators
|
209
|
+
|
210
|
+
In module representers you can add methods for properties.
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
module SongRepresenter
|
214
|
+
property :title
|
215
|
+
|
216
|
+
def title
|
217
|
+
@name
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
That works as the method is mixed into the represented object. Of course, this doesn't work with decorators.
|
222
|
+
|
223
|
+
Use `:getter` or `:setter` to dynamically add a method for the represented object.
|
224
|
+
|
225
|
+
```ruby
|
226
|
+
class SongRepresenter < Representable::Decorator
|
227
|
+
property :title, getter: lambda { |*| @name }
|
228
|
+
```
|
229
|
+
As always, the block is executed in the represented object's context.
|
230
|
+
|
208
231
|
## XML Support
|
209
232
|
|
210
233
|
While representable does a great job with JSON, it also features support for XML, YAML and pure ruby hashes.
|
data/TODO
CHANGED
@@ -25,4 +25,7 @@ module ReaderWriter
|
|
25
25
|
end
|
26
26
|
=> do that for all "features" (what parts would that be?: getter/setter, reader/writer, readable/writeable )?
|
27
27
|
|
28
|
-
* make lambda options optional (arity == 0)
|
28
|
+
* make lambda options optional (arity == 0)
|
29
|
+
|
30
|
+
* use `representer_exec` not only for lambdas, but also for methods
|
31
|
+
property :title # => calls decorator.title
|
@@ -12,10 +12,6 @@ module Representable
|
|
12
12
|
build_for(definition, *args)
|
13
13
|
end
|
14
14
|
|
15
|
-
def definition # TODO: remove in 1.4.
|
16
|
-
raise "Binding#definition is no longer supported as all Definition methods are now delegated automatically."
|
17
|
-
end
|
18
|
-
|
19
15
|
def initialize(definition, represented, user_options={}, lambda_context=represented) # TODO: remove default arg for user options. # DISCUSS: make lambda_context an options hash?
|
20
16
|
super(definition)
|
21
17
|
@represented = represented
|
data/lib/representable/hash.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'representable'
|
2
2
|
require 'representable/bindings/hash_bindings'
|
3
|
-
require 'json'
|
4
3
|
|
5
4
|
module Representable
|
6
5
|
# The generic representer. Brings #to_hash and #from_hash to your object.
|
@@ -13,28 +12,28 @@ module Representable
|
|
13
12
|
extend ClassMethods # DISCUSS: do that only for classes?
|
14
13
|
end
|
15
14
|
end
|
16
|
-
|
17
|
-
|
15
|
+
|
16
|
+
|
18
17
|
module ClassMethods
|
19
18
|
def from_hash(*args, &block)
|
20
19
|
create_represented(*args, &block).from_hash(*args)
|
21
20
|
end
|
22
21
|
end
|
23
|
-
|
24
|
-
|
22
|
+
|
23
|
+
|
25
24
|
def from_hash(data, options={}, binding_builder=PropertyBinding)
|
26
25
|
if wrap = options[:wrap] || representation_wrap
|
27
26
|
data = data[wrap.to_s]
|
28
27
|
end
|
29
|
-
|
28
|
+
|
30
29
|
update_properties_from(data, options, binding_builder)
|
31
30
|
end
|
32
|
-
|
31
|
+
|
33
32
|
def to_hash(options={}, binding_builder=PropertyBinding)
|
34
33
|
hash = create_representation_with({}, options, binding_builder)
|
35
|
-
|
34
|
+
|
36
35
|
return hash unless wrap = options[:wrap] || representation_wrap
|
37
|
-
|
36
|
+
|
38
37
|
{wrap => hash}
|
39
38
|
end
|
40
39
|
end
|
@@ -6,21 +6,20 @@ module Representable
|
|
6
6
|
attrs << Definition.new(*definition_opts) if attrs.size == 0
|
7
7
|
attrs
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def create_representation_with(doc, options, format)
|
11
11
|
bin = representable_bindings_for(format, options).first
|
12
|
-
hash = filter_keys_for(
|
12
|
+
hash = filter_keys_for(represented, options)
|
13
13
|
bin.write(doc, hash)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
def update_properties_from(doc, options, format)
|
17
17
|
bin = representable_bindings_for(format, options).first
|
18
18
|
hash = filter_keys_for(doc, options)
|
19
19
|
value = bin.deserialize_from(hash)
|
20
|
-
replace(value)
|
21
|
-
self
|
20
|
+
represented.replace(value)
|
22
21
|
end
|
23
|
-
|
22
|
+
|
24
23
|
private
|
25
24
|
def filter_keys_for(hash, options)
|
26
25
|
return hash unless props = options[:exclude] || options[:include]
|
@@ -1,33 +1,33 @@
|
|
1
1
|
module Representable::JSON
|
2
2
|
module Collection
|
3
3
|
include Representable::JSON
|
4
|
-
|
4
|
+
|
5
5
|
def self.included(base)
|
6
6
|
base.class_eval do
|
7
7
|
include Representable
|
8
8
|
extend ClassMethods
|
9
9
|
end
|
10
10
|
end
|
11
|
-
|
12
|
-
|
11
|
+
|
12
|
+
|
13
13
|
module ClassMethods
|
14
14
|
def items(options)
|
15
15
|
collection :_self, options
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
19
|
-
|
18
|
+
|
19
|
+
|
20
20
|
def create_representation_with(doc, options, format)
|
21
21
|
bin = representable_bindings_for(format, options).first
|
22
|
-
bin.serialize_for(
|
22
|
+
bin.serialize_for(represented)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def update_properties_from(doc, options, format)
|
26
26
|
bin = representable_bindings_for(format, options).first
|
27
27
|
value = bin.deserialize_from(doc)
|
28
|
-
replace(value)
|
28
|
+
represented.replace(value)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
# FIXME: refactor Definition so we can simply add options in #items to existing definition.
|
32
32
|
def representable_attrs
|
33
33
|
attrs = super
|
@@ -1,33 +1,33 @@
|
|
1
1
|
module Representable::XML
|
2
2
|
module Collection
|
3
3
|
include Representable::XML
|
4
|
-
|
4
|
+
|
5
5
|
def self.included(base)
|
6
6
|
base.class_eval do
|
7
7
|
include Representable
|
8
8
|
extend ClassMethods
|
9
9
|
end
|
10
10
|
end
|
11
|
-
|
12
|
-
|
11
|
+
|
12
|
+
|
13
13
|
module ClassMethods
|
14
14
|
def items(options)
|
15
15
|
collection :_self, options
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
19
|
-
|
18
|
+
|
19
|
+
|
20
20
|
def create_representation_with(doc, options, format)
|
21
21
|
bin = representable_bindings_for(format, options).first
|
22
|
-
bin.write(doc,
|
22
|
+
bin.write(doc, represented)
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
def update_properties_from(doc, options, format)
|
26
26
|
bin = representable_bindings_for(format, options).first
|
27
27
|
value = bin.deserialize_from(doc.search("./*")) # FIXME: use Binding#read.
|
28
|
-
replace(value)
|
28
|
+
represented.replace(value)
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
31
|
# FIXME: refactor Definition so we can simply add options in #items to existing definition.
|
32
32
|
def representable_attrs
|
33
33
|
attrs = super
|
data/representable.gemspec
CHANGED
@@ -23,8 +23,8 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.add_dependency "multi_json"
|
24
24
|
|
25
25
|
s.add_development_dependency "rake"
|
26
|
-
s.add_development_dependency "test_xml"
|
27
|
-
s.add_development_dependency "minitest", "
|
26
|
+
s.add_development_dependency "test_xml", ">= 0.1.6"
|
27
|
+
s.add_development_dependency "minitest", "~> 5.0.0"
|
28
28
|
s.add_development_dependency "mocha", ">= 0.13.0"
|
29
29
|
s.add_development_dependency "mongoid"
|
30
30
|
s.add_development_dependency "virtus", "~> 0.5.0"
|
data/test/json_test.rb
CHANGED
@@ -478,90 +478,107 @@ end
|
|
478
478
|
|
479
479
|
|
480
480
|
require 'representable/json/collection'
|
481
|
-
|
481
|
+
require 'representable/json/hash'
|
482
|
+
class LonelyRepresenterTest < MiniTest::Spec
|
482
483
|
module SongRepresenter
|
483
484
|
include Representable::JSON
|
484
485
|
property :name
|
485
486
|
end
|
486
487
|
|
488
|
+
let (:decorator) { rpr = representer; Class.new(Representable::Decorator) { include rpr } }
|
489
|
+
|
487
490
|
describe "JSON::Collection" do
|
488
491
|
describe "with contained objects" do
|
489
|
-
|
490
|
-
|
492
|
+
let (:representer) {
|
493
|
+
Module.new do
|
491
494
|
include Representable::JSON::Collection
|
492
495
|
items :class => Song, :extend => SongRepresenter
|
493
496
|
end
|
497
|
+
}
|
498
|
+
let (:songs) { [Song.new("Days Go By"), Song.new("Can't Take Them All")] }
|
499
|
+
let (:json) { "[{\"name\":\"Days Go By\"},{\"name\":\"Can't Take Them All\"}]" }
|
500
|
+
|
501
|
+
it "renders array" do
|
502
|
+
assert_json json, songs.extend(representer).to_json
|
494
503
|
end
|
495
504
|
|
496
|
-
it "renders
|
497
|
-
assert_json
|
505
|
+
it "renders array with decorator" do
|
506
|
+
assert_json json, decorator.new(songs).to_json
|
498
507
|
end
|
499
508
|
|
500
|
-
it "
|
501
|
-
|
509
|
+
it "parses array" do
|
510
|
+
[].extend(representer).from_json(json).must_equal songs
|
511
|
+
end
|
512
|
+
|
513
|
+
it "parses array with decorator" do
|
514
|
+
decorator.new([]).from_json(json).must_equal songs
|
502
515
|
end
|
503
516
|
end
|
504
517
|
|
505
518
|
describe "with contained text" do
|
506
|
-
|
507
|
-
|
519
|
+
let (:representer) {
|
520
|
+
Module.new do
|
508
521
|
include Representable::JSON::Collection
|
509
522
|
end
|
510
|
-
|
523
|
+
}
|
524
|
+
let (:songs) { ["Days Go By", "Can't Take Them All"] }
|
525
|
+
let (:json) { "[\"Days Go By\",\"Can't Take Them All\"]" }
|
511
526
|
|
512
527
|
it "renders contained items #to_json" do
|
513
|
-
assert_json
|
528
|
+
assert_json json, songs.extend(representer).to_json
|
514
529
|
end
|
515
530
|
|
516
531
|
it "returns objects array from #from_json" do
|
517
|
-
|
532
|
+
[].extend(representer).from_json(json).must_equal songs
|
518
533
|
end
|
519
534
|
end
|
520
535
|
end
|
521
|
-
end
|
522
536
|
|
523
537
|
|
524
|
-
require 'representable/json/hash'
|
525
|
-
class HashRepresenterTest < MiniTest::Spec
|
526
|
-
module SongRepresenter
|
527
|
-
include Representable::JSON
|
528
|
-
property :name
|
529
|
-
end
|
530
|
-
|
531
538
|
describe "JSON::Hash" do # TODO: move to HashTest.
|
532
539
|
describe "with contained objects" do
|
533
|
-
|
534
|
-
|
540
|
+
let (:representer) {
|
541
|
+
Module.new do
|
535
542
|
include Representable::JSON::Hash
|
536
543
|
values :class => Song, :extend => SongRepresenter
|
537
544
|
end
|
538
|
-
|
545
|
+
}
|
546
|
+
let (:json) { "{\"one\":{\"name\":\"Days Go By\"},\"two\":{\"name\":\"Can't Take Them All\"}}" }
|
547
|
+
let (:songs) { {"one" => Song.new("Days Go By"), "two" => Song.new("Can't Take Them All")} }
|
539
548
|
|
540
549
|
describe "#to_json" do
|
541
|
-
it "renders
|
542
|
-
|
550
|
+
it "renders hash" do
|
551
|
+
songs.extend(representer).to_json.must_equal json
|
552
|
+
end
|
553
|
+
|
554
|
+
it "renders hash with decorator" do
|
555
|
+
decorator.new(songs).to_json.must_equal json
|
543
556
|
end
|
544
557
|
|
545
558
|
it "respects :exclude" do
|
546
|
-
assert_json "{\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(
|
559
|
+
assert_json "{\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(representer).to_json(:exclude => [:one])
|
547
560
|
end
|
548
561
|
|
549
562
|
it "respects :include" do
|
550
|
-
assert_json "{\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(
|
563
|
+
assert_json "{\"two\":{\"name\":\"Can't Take Them All\"}}", {:one => Song.new("Days Go By"), :two => Song.new("Can't Take Them All")}.extend(representer).to_json(:include => [:two])
|
551
564
|
end
|
552
565
|
end
|
553
566
|
|
554
567
|
describe "#from_json" do
|
555
568
|
it "returns objects array" do
|
556
|
-
|
569
|
+
{}.extend(representer).from_json(json).must_equal songs
|
570
|
+
end
|
571
|
+
|
572
|
+
it "parses hash with decorator" do
|
573
|
+
decorator.new({}).from_json(json).must_equal songs
|
557
574
|
end
|
558
575
|
|
559
576
|
it "respects :exclude" do
|
560
|
-
assert_equal({"two" => Song.new("Can't Take Them All")}, {}.extend(
|
577
|
+
assert_equal({"two" => Song.new("Can't Take Them All")}, {}.extend(representer).from_json(json, :exclude => [:one]))
|
561
578
|
end
|
562
579
|
|
563
580
|
it "respects :include" do
|
564
|
-
assert_equal({"one" => Song.new("Days Go By")}, {}.extend(
|
581
|
+
assert_equal({"one" => Song.new("Days Go By")}, {}.extend(representer).from_json(json, :include => [:one]))
|
565
582
|
end
|
566
583
|
end
|
567
584
|
end
|
data/test/representable_test.rb
CHANGED
@@ -752,6 +752,12 @@ class RepresentableTest < MiniTest::Spec
|
|
752
752
|
album.wont_respond_to :to_hash
|
753
753
|
song.wont_respond_to :to_hash # DISCUSS: weak test, how to assert blank slate?
|
754
754
|
end
|
755
|
+
|
756
|
+
describe "#decorated" do
|
757
|
+
it "is aliased to #represented" do
|
758
|
+
AlbumRepresentation.prepare(album).decorated.must_equal album
|
759
|
+
end
|
760
|
+
end
|
755
761
|
end
|
756
762
|
|
757
763
|
describe "::prepare" do
|
data/test/test_helper.rb
CHANGED
data/test/xml_test.rb
CHANGED
@@ -5,7 +5,7 @@ class Band
|
|
5
5
|
include Representable::XML
|
6
6
|
property :name
|
7
7
|
attr_accessor :name
|
8
|
-
|
8
|
+
|
9
9
|
def initialize(name=nil)
|
10
10
|
name and self.name = name
|
11
11
|
end
|
@@ -14,12 +14,12 @@ end
|
|
14
14
|
class Album
|
15
15
|
attr_accessor :songs
|
16
16
|
end
|
17
|
-
|
18
|
-
|
17
|
+
|
18
|
+
|
19
19
|
class XmlTest < MiniTest::Spec
|
20
20
|
XML = Representable::XML
|
21
21
|
Def = Representable::Definition
|
22
|
-
|
22
|
+
|
23
23
|
describe "Xml module" do
|
24
24
|
before do
|
25
25
|
@Band = Class.new do
|
@@ -29,18 +29,18 @@ class XmlTest < MiniTest::Spec
|
|
29
29
|
property :label
|
30
30
|
attr_accessor :name, :label
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
@band = @Band.new
|
34
34
|
end
|
35
|
-
|
36
|
-
|
35
|
+
|
36
|
+
|
37
37
|
describe ".from_xml" do
|
38
38
|
it "is delegated to #from_xml" do
|
39
39
|
block = lambda {|*args|}
|
40
40
|
@Band.any_instance.expects(:from_xml).with("<document>", "options") # FIXME: how to NOT expect block?
|
41
41
|
@Band.from_xml("<document>", "options", &block)
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
it "yields new object and options to block" do
|
45
45
|
@Band.class_eval { attr_accessor :new_name }
|
46
46
|
@band = @Band.from_xml("<band/>", :new_name => "Diesel Boy") do |band, options|
|
@@ -49,15 +49,15 @@ class XmlTest < MiniTest::Spec
|
|
49
49
|
assert_equal "Diesel Boy", @band.new_name
|
50
50
|
end
|
51
51
|
end
|
52
|
-
|
53
|
-
|
52
|
+
|
53
|
+
|
54
54
|
describe ".from_node" do
|
55
55
|
it "is delegated to #from_node" do
|
56
56
|
block = lambda {|*args|}
|
57
57
|
@Band.any_instance.expects(:from_node).with("<document>", "options") # FIXME: how to expect block?
|
58
58
|
@Band.from_node("<document>", "options", &block)
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
61
|
it "yields new object and options to block" do
|
62
62
|
@Band.class_eval { attr_accessor :new_name }
|
63
63
|
@band = @Band.from_node(Nokogiri::XML("<band/>"), :new_name => "Diesel Boy") do |band, options|
|
@@ -66,105 +66,105 @@ class XmlTest < MiniTest::Spec
|
|
66
66
|
assert_equal "Diesel Boy", @band.new_name
|
67
67
|
end
|
68
68
|
end
|
69
|
-
|
70
|
-
|
69
|
+
|
70
|
+
|
71
71
|
describe "#from_xml" do
|
72
72
|
before do
|
73
73
|
@band = @Band.new
|
74
74
|
@xml = %{<band><name>Nofx</name><label>NOFX</label></band>}
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
it "parses XML and assigns properties" do
|
78
78
|
@band.from_xml(@xml)
|
79
79
|
assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
|
80
80
|
end
|
81
81
|
end
|
82
|
-
|
83
|
-
|
82
|
+
|
83
|
+
|
84
84
|
describe "#from_node" do
|
85
85
|
before do
|
86
86
|
@band = @Band.new
|
87
87
|
@xml = Nokogiri::XML(%{<band><name>Nofx</name><label>NOFX</label></band>}).root
|
88
88
|
end
|
89
|
-
|
89
|
+
|
90
90
|
it "receives Nokogiri node and assigns properties" do
|
91
91
|
@band.from_node(@xml)
|
92
92
|
assert_equal ["Nofx", "NOFX"], [@band.name, @band.label]
|
93
93
|
end
|
94
94
|
end
|
95
|
-
|
96
|
-
|
95
|
+
|
96
|
+
|
97
97
|
describe "#to_xml" do
|
98
98
|
it "delegates to #to_node and returns string" do
|
99
99
|
assert_xml_equal "<band><name>Rise Against</name></band>", Band.new("Rise Against").to_xml
|
100
100
|
end
|
101
101
|
end
|
102
|
-
|
103
|
-
|
102
|
+
|
103
|
+
|
104
104
|
describe "#to_node" do
|
105
105
|
it "returns Nokogiri node" do
|
106
106
|
node = Band.new("Rise Against").to_node
|
107
107
|
assert_kind_of Nokogiri::XML::Element, node
|
108
108
|
end
|
109
|
-
|
109
|
+
|
110
110
|
it "wraps with infered class name per default" do
|
111
111
|
node = Band.new("Rise Against").to_node
|
112
|
-
assert_xml_equal "<band><name>Rise Against</name></band>", node.to_s
|
112
|
+
assert_xml_equal "<band><name>Rise Against</name></band>", node.to_s
|
113
113
|
end
|
114
|
-
|
114
|
+
|
115
115
|
it "respects #representation_wrap=" do
|
116
116
|
klass = Class.new(Band) do
|
117
117
|
include Representable
|
118
118
|
property :name
|
119
119
|
end
|
120
|
-
|
120
|
+
|
121
121
|
klass.representation_wrap = :group
|
122
122
|
assert_xml_equal "<group><name>Rise Against</name></group>", klass.new("Rise Against").to_node.to_s
|
123
123
|
end
|
124
124
|
end
|
125
|
-
|
126
|
-
|
125
|
+
|
126
|
+
|
127
127
|
describe "XML::Binding#build_for" do
|
128
128
|
it "returns AttributeBinding" do
|
129
129
|
assert_kind_of XML::AttributeBinding, XML::PropertyBinding.build_for(Def.new(:band, :from => "band", :attribute => true), nil)
|
130
130
|
end
|
131
|
-
|
131
|
+
|
132
132
|
it "returns PropertyBinding" do
|
133
133
|
assert_kind_of XML::PropertyBinding, XML::PropertyBinding.build_for(Def.new(:band, :class => Hash), nil)
|
134
134
|
assert_kind_of XML::PropertyBinding, XML::PropertyBinding.build_for(Def.new(:band, :from => :content), nil)
|
135
135
|
end
|
136
|
-
|
136
|
+
|
137
137
|
it "returns CollectionBinding" do
|
138
138
|
assert_kind_of XML::CollectionBinding, XML::PropertyBinding.build_for(Def.new(:band, :collection => :true), nil)
|
139
139
|
end
|
140
|
-
|
140
|
+
|
141
141
|
it "returns HashBinding" do
|
142
142
|
assert_kind_of XML::HashBinding, XML::PropertyBinding.build_for(Def.new(:band, :hash => :true), nil)
|
143
143
|
end
|
144
144
|
end
|
145
|
-
|
146
|
-
|
145
|
+
|
146
|
+
|
147
147
|
describe "DCI" do
|
148
148
|
module SongRepresenter
|
149
149
|
include Representable::XML
|
150
150
|
property :name
|
151
151
|
representation_wrap = :song
|
152
152
|
end
|
153
|
-
|
153
|
+
|
154
154
|
module AlbumRepresenter
|
155
155
|
include Representable::XML
|
156
156
|
property :best_song, :class => Song, :extend => SongRepresenter
|
157
157
|
collection :songs, :class => Song, :from => :song, :extend => SongRepresenter
|
158
158
|
representation_wrap = :album
|
159
159
|
end
|
160
|
-
|
161
|
-
|
160
|
+
|
161
|
+
|
162
162
|
it "allows adding the representer by using #extend" do
|
163
163
|
module BandRepresenter
|
164
164
|
include Representable::XML
|
165
165
|
property :name
|
166
166
|
end
|
167
|
-
|
167
|
+
|
168
168
|
civ = Object.new
|
169
169
|
civ.instance_eval do
|
170
170
|
def name; "CIV"; end
|
@@ -172,26 +172,26 @@ class XmlTest < MiniTest::Spec
|
|
172
172
|
@name = v
|
173
173
|
end
|
174
174
|
end
|
175
|
-
|
175
|
+
|
176
176
|
civ.extend(BandRepresenter)
|
177
177
|
assert_xml_equal "<object><name>CIV</name></object>", civ.to_xml
|
178
178
|
end
|
179
|
-
|
179
|
+
|
180
180
|
it "extends contained models when serializing" do
|
181
181
|
@album = Album.new([Song.new("I Hate My Brain"), mr=Song.new("Mr. Charisma")], mr)
|
182
182
|
@album.extend(AlbumRepresenter)
|
183
|
-
|
183
|
+
|
184
184
|
assert_xml_equal "<album>
|
185
185
|
<song><name>Mr. Charisma</name></song>
|
186
186
|
<song><name>I Hate My Brain</name></song>
|
187
187
|
<song><name>Mr. Charisma</name></song>
|
188
188
|
</album>", @album.to_xml
|
189
189
|
end
|
190
|
-
|
190
|
+
|
191
191
|
it "extends contained models when deserializing" do
|
192
192
|
@album = Album.new
|
193
193
|
@album.extend(AlbumRepresenter)
|
194
|
-
|
194
|
+
|
195
195
|
@album.from_xml("<album><best_song><name>Mr. Charisma</name></best_song><song><name>I Hate My Brain</name></song><song><name>Mr. Charisma</name></song></album>")
|
196
196
|
assert_equal "Mr. Charisma", @album.best_song.name
|
197
197
|
end
|
@@ -208,7 +208,7 @@ class AttributesTest < MiniTest::Spec
|
|
208
208
|
property :title, :from => "title", :attribute => true
|
209
209
|
attr_accessor :href, :title
|
210
210
|
end
|
211
|
-
|
211
|
+
|
212
212
|
it "#from_xml creates correct accessors" do
|
213
213
|
link = Link.from_xml(%{
|
214
214
|
<a href="http://apotomo.de" title="Home, sweet home" />
|
@@ -216,11 +216,11 @@ class AttributesTest < MiniTest::Spec
|
|
216
216
|
assert_equal "http://apotomo.de", link.href
|
217
217
|
assert_equal "Home, sweet home", link.title
|
218
218
|
end
|
219
|
-
|
219
|
+
|
220
220
|
it "#to_xml serializes correctly" do
|
221
221
|
link = Link.new
|
222
222
|
link.href = "http://apotomo.de/"
|
223
|
-
|
223
|
+
|
224
224
|
assert_xml_equal %{<link href="http://apotomo.de/">}, link.to_xml
|
225
225
|
end
|
226
226
|
end
|
@@ -231,15 +231,15 @@ class TypedPropertyTest < MiniTest::Spec
|
|
231
231
|
include Representable::XML
|
232
232
|
property :band, :class => Band
|
233
233
|
end
|
234
|
-
|
235
|
-
|
234
|
+
|
235
|
+
|
236
236
|
class Album
|
237
237
|
attr_accessor :band
|
238
238
|
def initialize(band=nil)
|
239
239
|
@band = band
|
240
240
|
end
|
241
241
|
end
|
242
|
-
|
242
|
+
|
243
243
|
# TODO:property :group, :class => Band
|
244
244
|
# :class
|
245
245
|
# where to mixin DCI?
|
@@ -252,19 +252,19 @@ class TypedPropertyTest < MiniTest::Spec
|
|
252
252
|
})
|
253
253
|
assert_equal "Bad Religion", album.band.name
|
254
254
|
end
|
255
|
-
|
255
|
+
|
256
256
|
describe "#to_xml" do
|
257
257
|
it "doesn't escape xml from Band#to_xml" do
|
258
258
|
band = Band.new("Bad Religion")
|
259
259
|
album = Album.new(band).extend(AlbumRepresenter)
|
260
|
-
|
260
|
+
|
261
261
|
assert_xml_equal %{<album>
|
262
262
|
<band>
|
263
263
|
<name>Bad Religion</name>
|
264
264
|
</band>
|
265
265
|
</album>}, album.to_xml
|
266
266
|
end
|
267
|
-
|
267
|
+
|
268
268
|
it "doesn't escape and wrap string from Band#to_node" do
|
269
269
|
band = Band.new("Bad Religion")
|
270
270
|
band.instance_eval do
|
@@ -272,7 +272,7 @@ class TypedPropertyTest < MiniTest::Spec
|
|
272
272
|
"<band>Baaaad Religion</band>"
|
273
273
|
end
|
274
274
|
end
|
275
|
-
|
275
|
+
|
276
276
|
assert_xml_equal %{<album><band>Baaaad Religion</band></album>}, Album.new(band).extend(AlbumRepresenter).to_xml
|
277
277
|
end
|
278
278
|
end
|
@@ -287,7 +287,7 @@ class CollectionTest < MiniTest::Spec
|
|
287
287
|
collection :bands, :class => Band, :from => :band
|
288
288
|
attr_accessor :bands
|
289
289
|
end
|
290
|
-
|
290
|
+
|
291
291
|
describe "#from_xml" do
|
292
292
|
it "pushes collection items to array" do
|
293
293
|
cd = Compilation.from_xml(%{
|
@@ -298,7 +298,7 @@ class CollectionTest < MiniTest::Spec
|
|
298
298
|
})
|
299
299
|
assert_equal ["Cobra Skulls", "Diesel Boy"], cd.bands.map(&:name).sort
|
300
300
|
end
|
301
|
-
|
301
|
+
|
302
302
|
it "collections can be empty when default set" do
|
303
303
|
cd = Compilation.from_xml(%{
|
304
304
|
<compilation>
|
@@ -307,21 +307,21 @@ class CollectionTest < MiniTest::Spec
|
|
307
307
|
assert_equal [], cd.bands
|
308
308
|
end
|
309
309
|
end
|
310
|
-
|
310
|
+
|
311
311
|
it "responds to #to_xml" do
|
312
312
|
cd = Compilation.new
|
313
313
|
cd.bands = [Band.new("Diesel Boy"), Band.new("Bad Religion")]
|
314
|
-
|
314
|
+
|
315
315
|
assert_xml_equal %{<compilation>
|
316
316
|
<band><name>Diesel Boy</name></band>
|
317
317
|
<band><name>Bad Religion</name></band>
|
318
318
|
</compilation>}, cd.to_xml
|
319
319
|
end
|
320
320
|
end
|
321
|
-
|
322
|
-
|
321
|
+
|
322
|
+
|
323
323
|
describe ":from" do
|
324
|
-
let(:xml) {
|
324
|
+
let(:xml) {
|
325
325
|
Module.new do
|
326
326
|
include Representable::XML
|
327
327
|
collection :songs, :from => :song
|
@@ -362,7 +362,7 @@ class CollectionTest < MiniTest::Spec
|
|
362
362
|
assert_equal ["Laundry Basket", "Two Kevins", "Wright and Rong"].sort, album.songs.sort
|
363
363
|
end
|
364
364
|
end
|
365
|
-
|
365
|
+
|
366
366
|
describe "#to_xml" do
|
367
367
|
it "wraps items" do
|
368
368
|
album.songs = ["Laundry Basket", "Two Kevins", "Wright and Rong"]
|
@@ -380,12 +380,16 @@ class CollectionTest < MiniTest::Spec
|
|
380
380
|
end
|
381
381
|
|
382
382
|
require 'representable/xml/collection'
|
383
|
-
|
383
|
+
require 'representable/xml/hash'
|
384
|
+
class LonelyRepresenterTest < MiniTest::Spec
|
385
|
+
# TODO: where is the XML::Hash test?
|
384
386
|
module SongRepresenter
|
385
387
|
include Representable::XML
|
386
388
|
property :name
|
387
389
|
end
|
388
390
|
|
391
|
+
let (:decorator) { rpr = representer; Class.new(Representable::Decorator) { include rpr; self.representation_wrap= :songs } } # FIXME: why isn't representation wrap inherited properly?
|
392
|
+
|
389
393
|
describe "XML::Collection" do
|
390
394
|
describe "with contained objects" do
|
391
395
|
representer!(Representable::XML::Collection) do
|
@@ -393,48 +397,69 @@ class CollectionTest < MiniTest::Spec
|
|
393
397
|
self.representation_wrap= :songs
|
394
398
|
end
|
395
399
|
|
396
|
-
|
397
|
-
|
400
|
+
let (:songs) { [Song.new("Days Go By"), Song.new("Can't Take Them All")] }
|
401
|
+
let (:xml) { "<songs><song><name>Days Go By</name></song><song><name>Can't Take Them All</name></song></songs>" }
|
402
|
+
|
403
|
+
it "renders array" do
|
404
|
+
songs.extend(representer).to_xml.must_equal_xml xml
|
398
405
|
end
|
399
406
|
|
400
|
-
it "
|
401
|
-
|
407
|
+
it "renders array with decorator" do
|
408
|
+
decorator.new(songs).to_xml.must_equal_xml xml
|
409
|
+
end
|
410
|
+
|
411
|
+
it "parses array" do
|
412
|
+
[].extend(representer).from_xml(xml).must_equal songs
|
413
|
+
end
|
414
|
+
|
415
|
+
it "parses array with decorator" do
|
416
|
+
decorator.new([]).from_xml(xml).must_equal songs
|
402
417
|
end
|
403
418
|
end
|
404
419
|
end
|
405
|
-
end
|
406
420
|
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
self.representation_wrap= :favs
|
411
|
-
end
|
412
|
-
|
413
|
-
describe "#to_xml" do
|
414
|
-
it "renders values into attributes converting values to strings" do
|
415
|
-
assert_xml_equal "<favs one=\"Graveyards\" two=\"Can't Take Them All\" />", {:one => :Graveyards, :two => "Can't Take Them All"}.extend(representer).to_xml
|
416
|
-
end
|
417
|
-
|
418
|
-
it "respects :exclude" do
|
419
|
-
assert_xml_equal "<favs two=\"Can't Take Them All\" />", {:one => :Graveyards, :two => "Can't Take Them All"}.extend(representer).to_xml(:exclude => [:one])
|
421
|
+
describe "XML::AttributeHash" do # TODO: move to HashTest.
|
422
|
+
representer!(Representable::XML::AttributeHash) do
|
423
|
+
self.representation_wrap= :songs
|
420
424
|
end
|
421
|
-
|
422
|
-
|
423
|
-
|
425
|
+
|
426
|
+
let (:songs) { {"one" => "Graveyards", "two" => "Can't Take Them All"} }
|
427
|
+
let (:xml) { "<favs one=\"Graveyards\" two=\"Can't Take Them All\" />" }
|
428
|
+
|
429
|
+
describe "#to_xml" do
|
430
|
+
it "renders hash" do
|
431
|
+
songs.extend(representer).to_xml.must_equal_xml xml
|
432
|
+
end
|
433
|
+
|
434
|
+
it "respects :exclude" do
|
435
|
+
assert_xml_equal "<favs two=\"Can't Take Them All\" />", songs.extend(representer).to_xml(:exclude => [:one])
|
436
|
+
end
|
437
|
+
|
438
|
+
it "respects :include" do
|
439
|
+
assert_xml_equal "<favs two=\"Can't Take Them All\" />", songs.extend(representer).to_xml(:include => [:two])
|
440
|
+
end
|
441
|
+
|
442
|
+
it "renders hash with decorator" do
|
443
|
+
decorator.new(songs).to_xml.must_equal_xml xml
|
444
|
+
end
|
424
445
|
end
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
446
|
+
|
447
|
+
describe "#from_json" do
|
448
|
+
it "returns hash" do
|
449
|
+
{}.extend(representer).from_xml(xml).must_equal songs
|
450
|
+
end
|
451
|
+
|
452
|
+
it "respects :exclude" do
|
453
|
+
assert_equal({"two" => "Can't Take Them All"}, {}.extend(representer).from_xml(xml, :exclude => [:one]))
|
454
|
+
end
|
455
|
+
|
456
|
+
it "respects :include" do
|
457
|
+
assert_equal({"one" => "Graveyards"}, {}.extend(representer).from_xml(xml, :include => [:one]))
|
458
|
+
end
|
459
|
+
|
460
|
+
it "parses hash with decorator" do
|
461
|
+
decorator.new({}).from_xml(xml).must_equal songs
|
462
|
+
end
|
438
463
|
end
|
439
464
|
end
|
440
465
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: representable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05-
|
12
|
+
date: 2013-05-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
requirements:
|
67
67
|
- - ! '>='
|
68
68
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
69
|
+
version: 0.1.6
|
70
70
|
type: :development
|
71
71
|
prerelease: false
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -74,23 +74,23 @@ dependencies:
|
|
74
74
|
requirements:
|
75
75
|
- - ! '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version:
|
77
|
+
version: 0.1.6
|
78
78
|
- !ruby/object:Gem::Dependency
|
79
79
|
name: minitest
|
80
80
|
requirement: !ruby/object:Gem::Requirement
|
81
81
|
none: false
|
82
82
|
requirements:
|
83
|
-
- -
|
83
|
+
- - ~>
|
84
84
|
- !ruby/object:Gem::Version
|
85
|
-
version:
|
85
|
+
version: 5.0.0
|
86
86
|
type: :development
|
87
87
|
prerelease: false
|
88
88
|
version_requirements: !ruby/object:Gem::Requirement
|
89
89
|
none: false
|
90
90
|
requirements:
|
91
|
-
- -
|
91
|
+
- - ~>
|
92
92
|
- !ruby/object:Gem::Version
|
93
|
-
version:
|
93
|
+
version: 5.0.0
|
94
94
|
- !ruby/object:Gem::Dependency
|
95
95
|
name: mocha
|
96
96
|
requirement: !ruby/object:Gem::Requirement
|