representable 1.5.0 → 1.5.1
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.
- 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
|