representable 1.7.5 → 1.7.6

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 44bab0e189f2c4890389b5e7fbfdea850a4f3279
4
- data.tar.gz: 073892bb5e3a2da25b63f467f288e540e268e572
3
+ metadata.gz: 26bff301d2f96c009077a3675aabf598c4754a49
4
+ data.tar.gz: 6b21930dea9bf7bd327e536d78d2d4b71cb46110
5
5
  SHA512:
6
- metadata.gz: 2448f7cda7ea75e85a800fdfe99ffbfa394dc0ff69cdcdab36d02446987ef47c9e627f0477c27e9467558a08fa5d8d31c28bb42c31c1854c4c5d21b0fe33ce72
7
- data.tar.gz: 14f0bdb50cd844606957ea6e8ad3555e98f7bb5b2350aa6b3e1767f005ab3f44303d3d54ba6ca9344e20ba2bbeda1fe977771ca3f6da3bda13ac87c8efe510a6
6
+ metadata.gz: f28a2101a9eeac36b6c93edc4ceae8716b2f95918ed78ee5a9179727883ff42208b0041110f1bafbc3ba5b099e3ff46f547538a0f93e07156283254f7838147a
7
+ data.tar.gz: d2e5365454d29a47ee40829fbd3fe47bdbe0cccd4067493c26a4b700c50466ddb3ddece6f5e23fbb40d2d7437f928a3c051aab4256a666b1799e42ac2a2f15a8
@@ -3,4 +3,6 @@ notifications:
3
3
  matrix:
4
4
  include:
5
5
  - rvm: 1.9.3
6
+ - rvm: 2.0.0
7
+ - rvm: 2.1.0
6
8
 
data/CHANGES.md CHANGED
@@ -7,6 +7,11 @@ h2. 1.8.0
7
7
  -> make major steps lambda-able
8
8
  -> strategies for deserialization (lambda-able!)
9
9
 
10
+ h2. 1.7.6
11
+
12
+ * Add `::nested` to nest blocks in the document whilst still using the same represented object. Use with `Decorator` only.
13
+ * Fixing a bug (thanks @rsutphin) where inline decorators would inherit the properties from the outer decorator.
14
+
10
15
  h2. 1.7.5
11
16
 
12
17
  * propagate all options for ::property to ::inline_representer.
data/Gemfile CHANGED
@@ -3,5 +3,3 @@ source 'https://rubygems.org'
3
3
  gemspec
4
4
 
5
5
  #gem "virtus", :path => "../virtus"
6
- gem 'rake', "10.1.0"
7
- #gem 'debugger'
data/README.md CHANGED
@@ -9,7 +9,7 @@ Representable is helpful for all kind of rendering and parsing workflows. Howeve
9
9
 
10
10
  ## Installation
11
11
 
12
- The representable gem is almost dependency-free. Almost.
12
+ The representable gem runs with all Ruby versions >= 1.8.7.
13
13
 
14
14
  ```ruby
15
15
  gem 'representable'
@@ -233,6 +233,40 @@ module AlbumRepresenter
233
233
  end
234
234
  ```
235
235
 
236
+ ## Document Nesting
237
+
238
+ Not always does the structure of the desired document map to your objects. The `::nested` method allows you to structure properties in a separate section while still mapping the properties to the outer object.
239
+
240
+ Imagine the following document.
241
+
242
+ ```json
243
+ {"title": "Roxanne",
244
+ "details":
245
+ {"track": 3,
246
+ "length": "4:10"}
247
+ }
248
+ ```
249
+
250
+ However, both `track` and `length` are properties of the song object `<Song#0x999 title: "Roxanne", track: 3 ...>`, there is no such concept as `details` in the `Song` class. Representable gives you `::nested` to achieve this.
251
+
252
+ ```ruby
253
+ class SongRepresenter < Representable::Decorator
254
+ include Representable::JSON
255
+
256
+ property :title
257
+
258
+ nested :details do
259
+ property :track
260
+ property :length
261
+ end
262
+ end
263
+ ```
264
+
265
+ Just use an inline representer or the `extend:` option to define nested properties. Accessors for nested properties will still be called on the outer object (here, `song`). And as always, this works both ways for rendering and parsing.
266
+
267
+ Note that `nested` works with decorators, only. We might add it for modules soon.
268
+
269
+
236
270
  ## Decorator vs. Extend
237
271
 
238
272
  People who dislike `:extend` go use the `Decorator` strategy!
@@ -796,41 +830,44 @@ Coercing values only happens when rendering or parsing a document. Representable
796
830
 
797
831
  ## Undocumented Features
798
832
 
799
- (Please don't read this section!)
833
+ *(Please don't read this section!)*
834
+
835
+ ### Custom Binding
800
836
 
801
- * If you need a special binding for a property you're free to create it using the `:binding` option.
837
+ If you need a special binding for a property you're free to create it using the `:binding` option.
802
838
 
803
- <!-- here comes some code -->
804
839
  ```ruby
805
840
  property :title, :binding => lambda { |*args| JSON::TitleBinding.new(*args) }
806
841
  ```
807
842
 
808
- * You can use the parsed document fragment directly as a representable instance by returning `nil` in `:class`.
843
+ ### Syncing Parsing
844
+
845
+ You can use the parsed document fragment directly as a representable instance by returning `nil` in `:class`.
809
846
 
810
- <!-- here comes some code -->
811
847
  ```ruby
812
848
  property :song, :class => lambda { |*| nil }
813
849
  ```
814
850
 
815
851
  This makes sense when your parsing looks like this.
816
852
 
817
- <!-- here comes some code -->
818
853
  ```ruby
819
854
  hit.from_hash(song: <#Song ..>)
820
855
  ```
821
856
 
822
857
  Representable will not attempt to create a `Song` instance for you but use the provided from the document.
823
858
 
824
- * The same goes the other way when rendering. Just provide an empty `:instance` block.
859
+ Note that this is now the [official option](#syncing-objects) `:parse_strategy`.
860
+
861
+ ### Rendering Without Extend
862
+
863
+ The same goes the other way when rendering. Just provide an empty `:instance` block.
825
864
 
826
- <!-- here comes some code -->
827
865
  ```ruby
828
866
  property :song, :instance => lambda { |*| nil }
829
867
  ```
830
868
 
831
869
  This will treat the `song` property instance as a representable object.
832
870
 
833
- <!-- here comes some code -->
834
871
  ```ruby
835
872
  hit.to_json # this will call hit.song.to_json
836
873
  ```
@@ -128,7 +128,7 @@ private
128
128
  # collection :products, :from => :item
129
129
  # collection :products, :class => Product
130
130
  def collection(name, options={}, &block)
131
- options[:collection] = true
131
+ options[:collection] = true # FIXME: don't override original.
132
132
  property(name, options, &block)
133
133
  end
134
134
 
@@ -7,17 +7,36 @@ module Representable
7
7
  new(represented)
8
8
  end
9
9
 
10
- def self.inline_representer(base_module, name, options, &block) # DISCUSS: separate module?
11
- Class.new(self) do
12
- include base_module
13
- instance_exec &block
10
+ def self.inline_representer(base_module, name, options, &block)
11
+ # FIXME: it is wrong to inherit from self here as we just want to "inherit" the included modules but nothing else.
12
+ Class.new(self).tap do |decorator|
13
+ decorator.class_eval do # Ruby 1.8.7 wouldn't properly execute the block passed to Class.new!
14
+ # Remove parent's property definitions before defining the inline ones. #FIXME: don't inherit from self, remove those 2 lines.
15
+ representable_attrs.clear
16
+ representable_attrs.inheritable_arrays.clear
17
+
18
+ include base_module
19
+ instance_exec &block
20
+ end
14
21
  end
15
22
  end
16
23
 
24
+ # Allows you to nest a block of properties in a separate section while still mapping them to the outer object.
25
+ def self.nested(name, options={}, &block)
26
+ options = options.merge(
27
+ :nested => true,
28
+ :getter => lambda { |*| self },
29
+ :setter => lambda { |*| },
30
+ :instance => lambda { |*| self }
31
+ )
32
+
33
+ property(name, options, &block)
34
+ end
35
+
17
36
  include Representable # include after class methods so Decorator::prepare can't be overwritten by Representable::prepare.
18
37
 
19
38
  def initialize(represented)
20
39
  @represented = represented
21
40
  end
22
41
  end
23
- end
42
+ end
@@ -1,3 +1,3 @@
1
1
  module Representable
2
- VERSION = "1.7.5"
2
+ VERSION = "1.7.6"
3
3
  end
@@ -11,8 +11,8 @@ Gem::Specification.new do |s|
11
11
  s.authors = ["Nick Sutterer"]
12
12
  s.email = ["apotonick@gmail.com"]
13
13
  s.homepage = "https://github.com/apotonick/representable/"
14
- s.summary = %q{Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties, collections and compositions.}
15
- s.description = %q{Maps representation documents from and to Ruby objects. Includes XML and JSON support, plain properties, collections and compositions.}
14
+ s.summary = %q{Renders and parses JSON/XML/YAML documents from and to Ruby objects. Includes plain properties, collections, nesting, coercion and more.}
15
+ s.description = s.summary
16
16
 
17
17
  s.files = `git ls-files`.split("\n")
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -21,7 +21,54 @@ class GenericTest < MiniTest::Spec
21
21
  it "leaves properties untouched" do
22
22
  album.from_hash({})
23
23
  # TODO: test property.
24
- album.songs.must_equal ["Fuck Armageddon"] # DISCUSS: do we really want this?
24
+ album.songs.must_equal ["Fuck Armageddon"] # when the collection is not present in the incoming hash, this propery stays untouched.
25
+ end
26
+
27
+
28
+ # when collection is nil, it doesn't get rendered:
29
+ for_formats(
30
+ :hash => [Representable::Hash, {}],
31
+ :xml => [Representable::XML, "<open_struct></open_struct>"],
32
+ :yaml => [Representable::YAML, "--- {}\n"], # FIXME: this doesn't look right.
33
+ ) do |format, mod, output, input|
34
+
35
+ describe "nil collections" do
36
+ let (:format) { format }
37
+
38
+ representer!(:module => mod) do
39
+ collection :songs
40
+ self.representation_wrap = :album if format == :xml
41
+ end
42
+
43
+ let (:album) { Album.new.extend(representer) }
44
+
45
+ it "doesn't render collection in #{format}" do
46
+ render(album).must_equal_document output
47
+ end
48
+ end
49
+ end
50
+
51
+ # when collection is set but empty, render the empty collection.
52
+ for_formats(
53
+ :hash => [Representable::Hash, {"songs" => []}],
54
+ #:xml => [Representable::XML, "<open_struct><songs/></open_struct>"],
55
+ :yaml => [Representable::YAML, "---\nsongs: []\n"],
56
+ ) do |format, mod, output, input|
57
+
58
+ describe "empty collections" do
59
+ let (:format) { format }
60
+
61
+ representer!(:module => mod) do
62
+ collection :songs
63
+ self.representation_wrap = :album if format == :xml
64
+ end
65
+
66
+ let (:album) { OpenStruct.new(:songs => []).extend(representer) }
67
+
68
+ it "renders empty collection in #{format}" do
69
+ render(album).must_equal_document output
70
+ end
71
+ end
25
72
  end
26
73
  end
27
74
 
@@ -45,13 +92,6 @@ class GenericTest < MiniTest::Spec
45
92
  end
46
93
  end
47
94
 
48
- def self.for_formats(formats)
49
- formats.each do |format, cfg|
50
- mod, output, input = cfg
51
- yield format, mod, output, input
52
- end
53
- end
54
-
55
95
 
56
96
  for_formats(
57
97
  :hash => [Representable::Hash, {"song"=>{"title"=>"Resist Stance"}}, {"song"=>{"title"=>"Suffer"}}],
@@ -129,27 +169,6 @@ class GenericTest < MiniTest::Spec
129
169
  end
130
170
  end
131
171
 
132
- def render(object)
133
- AssertableDocument.new(object.send("to_#{format}"), format)
134
- end
135
-
136
- def parse(object, input)
137
- object.send("from_#{format}", input)
138
- end
139
-
140
- class AssertableDocument
141
- attr_reader :document
142
-
143
- def initialize(document, format)
144
- @document, @format = document, format
145
- end
146
-
147
- def must_equal_document(*args)
148
- return document.must_equal_xml(*args) if @format == :xml
149
- document.must_equal(*args)
150
- end
151
- end
152
-
153
172
 
154
173
  # Lonely Collection
155
174
  require "representable/hash/collection"
@@ -0,0 +1,144 @@
1
+ require 'test_helper'
2
+
3
+ class InlineTest < MiniTest::Spec
4
+ let (:song) { Song.new("Alive") }
5
+ let (:request) { representer.prepare(OpenStruct.new(:song => song)) }
6
+
7
+ {
8
+ :hash => [Representable::Hash, {"song"=>{"name"=>"Alive"}}, {"song"=>{"name"=>"You've Taken Everything"}}],
9
+ :json => [Representable::JSON, "{\"song\":{\"name\":\"Alive\"}}", "{\"song\":{\"name\":\"You've Taken Everything\"}}"],
10
+ :xml => [Representable::XML, "<open_struct>\n <song>\n <name>Alive</name>\n </song>\n</open_struct>", "<open_struct><song><name>You've Taken Everything</name></song>/open_struct>"],
11
+ :yaml => [Representable::YAML, "---\nsong:\n name: Alive\n", "---\nsong:\n name: You've Taken Everything\n"],
12
+ }.each do |format, cfg|
13
+ mod, output, input = cfg
14
+
15
+ describe "[#{format}] with :class" do
16
+ representer!(:module => mod) do
17
+ property :song, :class => Song do
18
+ property :name
19
+ end
20
+ end
21
+
22
+ it { request.send("to_#{format}").must_equal output }
23
+ it { request.send("from_#{format}", input).song.name.must_equal "You've Taken Everything"}
24
+ end
25
+ end
26
+
27
+ {
28
+ :hash => [Representable::Hash, {"songs"=>[{"name"=>"Alive"}]}, {"songs"=>[{"name"=>"You've Taken Everything"}]}],
29
+ :json => [Representable::JSON, "{\"songs\":[{\"name\":\"Alive\"}]}", "{\"songs\":[{\"name\":\"You've Taken Everything\"}]}"],
30
+ :xml => [Representable::XML, "<open_struct>\n <song>\n <name>Alive</name>\n </song>\n</open_struct>", "<open_struct><song><name>You've Taken Everything</name></song></open_struct>", { :from => :song }],
31
+ :yaml => [Representable::YAML, "---\nsongs:\n- name: Alive\n", "---\nsongs:\n- name: You've Taken Everything\n"],
32
+ }.each do |format, cfg|
33
+ mod, output, input, collection_options = cfg
34
+ collection_options ||= {}
35
+
36
+ describe "[#{format}] collection with :class" do
37
+ let (:request) { representer.prepare(OpenStruct.new(:songs => [song])) }
38
+
39
+ representer!(:module => mod) do
40
+ collection :songs, collection_options.merge(:class => Song) do
41
+ property :name
42
+ end
43
+ end
44
+
45
+ it { request.send("to_#{format}").must_equal output }
46
+ it { request.send("from_#{format}", input).songs.first.name.must_equal "You've Taken Everything"}
47
+ end
48
+ end
49
+
50
+ describe "without :class" do
51
+ representer! do
52
+ property :song do
53
+ property :name
54
+ end
55
+ end
56
+
57
+ it { request.to_hash.must_equal({"song"=>{"name"=>"Alive"}}) }
58
+ end
59
+
60
+ describe "decorator" do
61
+ let (:request) { representer.prepare(OpenStruct.new(:song => song, :who => "Josephine")) }
62
+
63
+ let (:representer) do
64
+ Class.new(Representable::Decorator) do
65
+ include Representable::Hash
66
+
67
+ property :who
68
+
69
+ property :song, :class => Song do
70
+ property :name
71
+ end
72
+
73
+ self
74
+ end
75
+ end
76
+
77
+ it { request.to_hash.must_equal({"who"=>"Josephine", "song"=>{"name"=>"Alive"}}) }
78
+ it { request.from_hash({"song"=>{"name"=>"You've Taken Everything"}}).song.name.must_equal "You've Taken Everything"}
79
+
80
+ it "uses an inline decorator" do
81
+ request.to_hash
82
+ song.wont_be_kind_of Representable
83
+ end
84
+ end
85
+
86
+ # TODO: should be in extend:/decorator: test.
87
+ # FIXME: this tests :getter{represented}+:extend - represented gets extended twice and the inline decorator overrides original config.
88
+ # for_formats(
89
+ # :hash => [Representable::Hash, {"album" => {"artist" => {"label"=>"Epitaph"}}}],
90
+ # # :xml => [Representable::XML, "<open_struct></open_struct>"],
91
+ # #:yaml => [Representable::YAML, "---\nlabel:\n label: Epitaph\n owner: Brett Gurewitz\n"]
92
+ # ) do |format, mod, output, input|
93
+
94
+ # module ArtistRepresenter
95
+ # include Representable::JSON
96
+ # property :label
97
+ # end
98
+
99
+ # describe ":getter with inline representer" do
100
+ # let (:format) { format }
101
+
102
+ # representer!(:module => mod) do
103
+ # self.representation_wrap = :album
104
+
105
+ # property :artist, :getter => lambda { |args| represented }, :extend => ArtistRepresenter
106
+ # end
107
+
108
+ # let (:album) { OpenStruct.new(:label => "Epitaph").extend(representer) }
109
+
110
+ # it "renders nested Album-properties in separate section" do
111
+ # render(album).must_equal_document output
112
+ # end
113
+ # end
114
+ # end
115
+
116
+
117
+ for_formats({
118
+ :hash => [Representable::Hash, {"album" => {"artist" => {"label"=>"Epitaph"}}}],
119
+ # :xml => [Representable::XML, "<open_struct></open_struct>"],
120
+ #:yaml => [Representable::YAML, "---\nlabel:\n label: Epitaph\n owner: Brett Gurewitz\n"]
121
+ }) do |format, mod, output, input|
122
+
123
+ class ArtistDecorator < Representable::Decorator
124
+ include Representable::JSON
125
+ property :label
126
+ end
127
+
128
+ describe ":getter with :decorator" do
129
+ let (:format) { format }
130
+
131
+ representer!(:module => mod) do
132
+ self.representation_wrap = "album"
133
+
134
+ property :artist, :getter => lambda { |args| represented }, :decorator => ArtistDecorator
135
+ end
136
+
137
+ let (:album) { OpenStruct.new(:label => "Epitaph").extend(representer) }
138
+
139
+ it "renders nested Album-properties in separate section" do
140
+ render(album).must_equal_document output
141
+ end
142
+ end
143
+ end
144
+ end
@@ -426,7 +426,7 @@ end
426
426
 
427
427
  class HashTest < MiniTest::Spec
428
428
  describe "hash :songs" do
429
- representer!(Representable::JSON) do
429
+ representer!(:module => Representable::JSON) do
430
430
  hash :songs
431
431
  end
432
432
 
@@ -443,7 +443,7 @@ end
443
443
  end
444
444
 
445
445
  describe "hash :songs, :class => Song" do
446
- representer!(Representable::JSON) do
446
+ representer!(:module => Representable::JSON) do
447
447
  hash :songs, :extend => Module.new { include Representable::JSON; property :name }, :class => Song
448
448
  end
449
449
 
@@ -0,0 +1,97 @@
1
+ require 'test_helper'
2
+
3
+ class NestedTest < MiniTest::Spec
4
+ Album = Struct.new(:label, :owner)
5
+
6
+ for_formats(
7
+ :hash => [Representable::Hash, {"label" => {"label"=>"Epitaph", "owner"=>"Brett Gurewitz"}}],
8
+ # :xml => [Representable::XML, "<open_struct></open_struct>"],
9
+ :yaml => [Representable::YAML, "---\nlabel:\n label: Epitaph\n owner: Brett Gurewitz\n"]
10
+ ) do |format, mod, output, input|
11
+
12
+ # describe "::nested with inline representer" do
13
+ # let (:format) { format }
14
+
15
+ # representer!(:module => mod) do
16
+ # nested :label do
17
+ # property :label
18
+ # property :owner
19
+
20
+ # # self.representation_wrap = nil if format == :xml
21
+ # end
22
+
23
+
24
+ # self.representation_wrap = :album if format == :xml
25
+ # end
26
+
27
+ # let (:album) { Album.new("Epitaph", "Brett Gurewitz").extend(representer) }
28
+
29
+ # it "renders nested Album-properties in separate section" do
30
+ # render(album).must_equal_document output
31
+ # end
32
+
33
+ # it "parses nested properties to Album instance" do
34
+ # album = parse(Album.new.extend(representer), output)
35
+ # album.label.must_equal "Epitaph"
36
+ # album.owner.must_equal "Brett Gurewitz"
37
+ # end
38
+ # end
39
+
40
+ describe "Decorator ::nested with extend:" do
41
+ let (:format) { format }
42
+
43
+ representer!(:name => :label_rpr) do
44
+ include mod
45
+ property :label
46
+ property :owner
47
+ end
48
+
49
+ representer!(:module => mod, :decorator => true, :inject => :label_rpr) do
50
+ nested :label, :extend => label_rpr
51
+
52
+ self.representation_wrap = :album if format == :xml
53
+ end
54
+
55
+ let (:album) { representer.prepare(Album.new("Epitaph", "Brett Gurewitz")) }
56
+
57
+ # TODO: shared example with above.
58
+ it "renders nested Album-properties in separate section" do
59
+ render(album).must_equal_document output
60
+ end
61
+
62
+ it "parses nested properties to Album instance" do
63
+ album = parse(representer.prepare(Album.new), output)
64
+ album.label.must_equal "Epitaph"
65
+ album.owner.must_equal "Brett Gurewitz"
66
+ end
67
+ end
68
+
69
+ describe "Decorator ::nested" do
70
+ let (:format) { format }
71
+
72
+ representer!(:module => mod, :decorator => true) do
73
+ nested :label do
74
+ property :label
75
+ property :owner
76
+
77
+ # self.representation_wrap = nil if format == :xml
78
+ end
79
+
80
+
81
+ self.representation_wrap = :album if format == :xml
82
+ end
83
+
84
+ let (:album) { representer.prepare(Album.new("Epitaph", "Brett Gurewitz")) }
85
+
86
+ it "renders nested Album-properties in separate section" do
87
+ render(album).must_equal_document output
88
+ end
89
+
90
+ it "parses nested properties to Album instance" do
91
+ album = parse(representer.prepare(Album.new), output)
92
+ album.label.must_equal "Epitaph"
93
+ album.owner.must_equal "Brett Gurewitz"
94
+ end
95
+ end
96
+ end
97
+ end
@@ -457,86 +457,6 @@ class RepresentableTest < MiniTest::Spec
457
457
  end
458
458
  end
459
459
 
460
- describe "inline representers" do
461
- let (:song) { Song.new("Alive") }
462
- let (:request) { representer.prepare(OpenStruct.new(:song => song)) }
463
-
464
- {
465
- :hash => [Representable::Hash, {"song"=>{"name"=>"Alive"}}, {"song"=>{"name"=>"You've Taken Everything"}}],
466
- :json => [Representable::JSON, "{\"song\":{\"name\":\"Alive\"}}", "{\"song\":{\"name\":\"You've Taken Everything\"}}"],
467
- :xml => [Representable::XML, "<open_struct>\n <song>\n <name>Alive</name>\n </song>\n</open_struct>", "<open_struct><song><name>You've Taken Everything</name></song>/open_struct>"],
468
- :yaml => [Representable::YAML, "---\nsong:\n name: Alive\n", "---\nsong:\n name: You've Taken Everything\n"],
469
- }.each do |format, cfg|
470
- mod, output, input = cfg
471
-
472
- describe "[#{format}] with :class" do
473
- representer!(mod) do
474
- property :song, :class => Song do
475
- property :name
476
- end
477
- end
478
-
479
- it { request.send("to_#{format}").must_equal output }
480
- it { request.send("from_#{format}", input).song.name.must_equal "You've Taken Everything"}
481
- end
482
- end
483
-
484
- {
485
- :hash => [Representable::Hash, {"songs"=>[{"name"=>"Alive"}]}, {"songs"=>[{"name"=>"You've Taken Everything"}]}],
486
- :json => [Representable::JSON, "{\"songs\":[{\"name\":\"Alive\"}]}", "{\"songs\":[{\"name\":\"You've Taken Everything\"}]}"],
487
- :xml => [Representable::XML, "<open_struct>\n <song>\n <name>Alive</name>\n </song>\n</open_struct>", "<open_struct><song><name>You've Taken Everything</name></song></open_struct>", { :from => :song }],
488
- :yaml => [Representable::YAML, "---\nsongs:\n- name: Alive\n", "---\nsongs:\n- name: You've Taken Everything\n"],
489
- }.each do |format, cfg|
490
- mod, output, input, collection_options = cfg
491
- collection_options ||= {}
492
-
493
- describe "[#{format}] collection with :class" do
494
- let (:request) { representer.prepare(OpenStruct.new(:songs => [song])) }
495
-
496
- representer!(mod) do
497
- collection :songs, collection_options.merge(:class => Song) do
498
- property :name
499
- end
500
- end
501
-
502
- it { request.send("to_#{format}").must_equal output }
503
- it { request.send("from_#{format}", input).songs.first.name.must_equal "You've Taken Everything"}
504
- end
505
- end
506
-
507
- describe "without :class" do
508
- representer! do
509
- property :song do
510
- property :name
511
- end
512
- end
513
-
514
- it { request.to_hash.must_equal({"song"=>{"name"=>"Alive"}}) }
515
- end
516
-
517
- describe "decorator" do
518
- let (:representer) do
519
- Class.new(Representable::Decorator) do
520
- include Representable::Hash
521
-
522
- property :song, :class => Song do
523
- property :name
524
- end
525
-
526
- self
527
- end
528
- end
529
-
530
- it { request.to_hash.must_equal({"song"=>{"name"=>"Alive"}}) }
531
- it { request.from_hash({"song"=>{"name"=>"You've Taken Everything"}}).song.name.must_equal "You've Taken Everything"}
532
-
533
- it "uses an inline decorator" do
534
- request.to_hash
535
- song.wont_be_kind_of Representable
536
- end
537
- end
538
- end
539
-
540
460
  describe ":if" do
541
461
  before do
542
462
  @pop = Class.new(PopBand) { attr_accessor :fame }
@@ -49,20 +49,44 @@ MiniTest::Spec.class_eval do
49
49
  include AssertJson::Assertions
50
50
  include XmlHelper
51
51
 
52
- def self.representer!(format=Representable::Hash, name=:representer, &block)
53
- fmt = format # we need that so the 2nd call to ::let (within a ::describe) remembers the right format.
52
+ def self.for_formats(formats)
53
+ formats.each do |format, cfg|
54
+ mod, output, input = cfg
55
+ yield format, mod, output, input
56
+ end
57
+ end
58
+
59
+ def render(object)
60
+ AssertableDocument.new(object.send("to_#{format}"), format)
61
+ end
62
+
63
+ def parse(object, input)
64
+ object.send("from_#{format}", input)
65
+ end
66
+
67
+ class AssertableDocument
68
+ attr_reader :document
54
69
 
55
- if fmt.is_a?(Hash)
56
- name = fmt[:name] || :representer
57
- format = fmt[:module] || Representable::Hash
70
+ def initialize(document, format)
71
+ @document, @format = document, format
58
72
  end
59
73
 
74
+ def must_equal_document(*args)
75
+ return document.must_equal_xml(*args) if @format == :xml
76
+ document.must_equal(*args)
77
+ end
78
+ end
79
+
80
+ def self.representer!(options={}, &block)
81
+ fmt = options # we need that so the 2nd call to ::let (within a ::describe) remembers the right format.
82
+
83
+ name = options[:name] || :representer
84
+ format = options[:module] || Representable::Hash
85
+
60
86
  let(name) do
61
- mod = Module.new
87
+ mod = options[:decorator] ? Class.new(Representable::Decorator) : Module.new
62
88
 
63
- if fmt.is_a?(Hash)
64
- inject_representer(mod, fmt)
65
- end
89
+ inject_representer(mod, fmt)
66
90
 
67
91
  mod.module_eval do
68
92
  include format
@@ -396,7 +396,7 @@ class CollectionTest < MiniTest::Spec
396
396
 
397
397
  describe "XML::Collection" do
398
398
  describe "with contained objects" do
399
- representer!(Representable::XML::Collection) do
399
+ representer!(:module => Representable::XML::Collection) do
400
400
  items :class => Song, :extend => SongRepresenter
401
401
  self.representation_wrap= :songs
402
402
  end
@@ -423,7 +423,7 @@ class CollectionTest < MiniTest::Spec
423
423
  end
424
424
 
425
425
  describe "XML::AttributeHash" do # TODO: move to HashTest.
426
- representer!(Representable::XML::AttributeHash) do
426
+ representer!(:module => Representable::XML::AttributeHash) do
427
427
  self.representation_wrap= :songs
428
428
  end
429
429
 
metadata CHANGED
@@ -1,165 +1,165 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: representable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.5
4
+ version: 1.7.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-18 00:00:00.000000000 Z
11
+ date: 2014-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: multi_json
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: test_xml
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: 0.1.6
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: 0.1.6
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: minitest
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ~>
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
75
  version: 5.0.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ~>
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 5.0.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: mocha
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - '>='
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: 0.13.0
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - '>='
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: 0.13.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: mongoid
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - '>='
101
+ - - ">="
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - '>='
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: virtus
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ~>
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
117
  version: 0.5.0
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ~>
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: 0.5.0
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: yajl-ruby
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - '>='
129
+ - - ">="
130
130
  - !ruby/object:Gem::Version
131
131
  version: '0'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - '>='
136
+ - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: json
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
- - - ~>
143
+ - - "~>"
144
144
  - !ruby/object:Gem::Version
145
145
  version: 1.7.7
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
- - - ~>
150
+ - - "~>"
151
151
  - !ruby/object:Gem::Version
152
152
  version: 1.7.7
153
- description: Maps representation documents from and to Ruby objects. Includes XML
154
- and JSON support, plain properties, collections and compositions.
153
+ description: Renders and parses JSON/XML/YAML documents from and to Ruby objects.
154
+ Includes plain properties, collections, nesting, coercion and more.
155
155
  email:
156
156
  - apotonick@gmail.com
157
157
  executables: []
158
158
  extensions: []
159
159
  extra_rdoc_files: []
160
160
  files:
161
- - .gitignore
162
- - .travis.yml
161
+ - ".gitignore"
162
+ - ".travis.yml"
163
163
  - CHANGES.md
164
164
  - Gemfile
165
165
  - LICENSE
@@ -203,8 +203,10 @@ files:
203
203
  - test/hash_bindings_test.rb
204
204
  - test/hash_test.rb
205
205
  - test/inheritance_test.rb
206
+ - test/inline_test.rb
206
207
  - test/json_test.rb
207
208
  - test/mongoid_test.rb
209
+ - test/nested_test.rb
208
210
  - test/representable_test.rb
209
211
  - test/test_helper.rb
210
212
  - test/test_helper_test.rb
@@ -220,19 +222,38 @@ require_paths:
220
222
  - lib
221
223
  required_ruby_version: !ruby/object:Gem::Requirement
222
224
  requirements:
223
- - - '>='
225
+ - - ">="
224
226
  - !ruby/object:Gem::Version
225
227
  version: '0'
226
228
  required_rubygems_version: !ruby/object:Gem::Requirement
227
229
  requirements:
228
- - - '>='
230
+ - - ">="
229
231
  - !ruby/object:Gem::Version
230
232
  version: '0'
231
233
  requirements: []
232
234
  rubyforge_project:
233
- rubygems_version: 2.0.3
235
+ rubygems_version: 2.2.1
234
236
  signing_key:
235
237
  specification_version: 4
236
- summary: Maps representation documents from and to Ruby objects. Includes XML and
237
- JSON support, plain properties, collections and compositions.
238
- test_files: []
238
+ summary: Renders and parses JSON/XML/YAML documents from and to Ruby objects. Includes
239
+ plain properties, collections, nesting, coercion and more.
240
+ test_files:
241
+ - test/coercion_test.rb
242
+ - test/config_test.rb
243
+ - test/decorator_test.rb
244
+ - test/definition_test.rb
245
+ - test/example.rb
246
+ - test/generic_test.rb
247
+ - test/hash_bindings_test.rb
248
+ - test/hash_test.rb
249
+ - test/inheritance_test.rb
250
+ - test/inline_test.rb
251
+ - test/json_test.rb
252
+ - test/mongoid_test.rb
253
+ - test/nested_test.rb
254
+ - test/representable_test.rb
255
+ - test/test_helper.rb
256
+ - test/test_helper_test.rb
257
+ - test/xml_bindings_test.rb
258
+ - test/xml_test.rb
259
+ - test/yaml_test.rb