representable 1.7.5 → 1.7.6

Sign up to get free protection for your applications and to get access to all the features.
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