representable 2.0.4 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,48 @@
1
+ require 'representable/hash/binding'
2
+
3
+ module Representable
4
+ module YAML
5
+ class Binding < Representable::Hash::Binding
6
+ def self.build_for(definition, *args)
7
+ return Collection.new(definition, *args) if definition.array?
8
+ new(definition, *args)
9
+ end
10
+
11
+ def write(map, fragment)
12
+ map.children << Psych::Nodes::Scalar.new(as)
13
+ map.children << node_for(fragment) # FIXME: should be serialize.
14
+ end
15
+ # private
16
+
17
+ def node_for(fragment)
18
+ write_scalar(fragment)
19
+ end
20
+
21
+ def write_scalar(value)
22
+ return value if typed?
23
+
24
+ Psych::Nodes::Scalar.new(value.to_s)
25
+ end
26
+
27
+ def serialize_method
28
+ :to_ast
29
+ end
30
+
31
+ def deserialize_method
32
+ :from_hash
33
+ end
34
+
35
+
36
+ class Collection < self
37
+ include Representable::Binding::Collection
38
+
39
+ def node_for(fragments)
40
+ Psych::Nodes::Sequence.new.tap do |seq|
41
+ seq.style = Psych::Nodes::Sequence::FLOW if self[:style] == :flow
42
+ fragments.each { |frag| seq.children << write_scalar(frag) }
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -25,7 +25,7 @@ Gem::Specification.new do |s|
25
25
 
26
26
  s.add_development_dependency "rake"
27
27
  s.add_development_dependency "test_xml", ">= 0.1.6"
28
- s.add_development_dependency "minitest", "~> 5.0.0"
28
+ s.add_development_dependency "minitest", ">= 5.4.1"
29
29
  s.add_development_dependency "mocha", ">= 0.13.0"
30
30
  s.add_development_dependency "mongoid"
31
31
  s.add_development_dependency "virtus"
@@ -0,0 +1,83 @@
1
+ require 'test_helper'
2
+ require 'benchmark'
3
+
4
+ SONG_PROPERTIES = 1000.times.collect do |i|
5
+ "property_#{i}"
6
+ end
7
+
8
+
9
+ module SongRepresenter
10
+ include Representable::JSON
11
+
12
+ SONG_PROPERTIES.each { |p| property p }
13
+ end
14
+
15
+ class SongDecorator < Representable::Decorator
16
+ include Representable::JSON
17
+
18
+ SONG_PROPERTIES.each { |p| property p }
19
+ end
20
+
21
+ module AlbumRepresenter
22
+ include Representable::JSON
23
+
24
+ # collection :songs, extend: SongRepresenter
25
+ collection :songs, extend: SongDecorator
26
+ end
27
+
28
+ def random_song
29
+ attrs = Hash[SONG_PROPERTIES.collect { |n| [n,n] }]
30
+ OpenStruct.new(attrs)
31
+ end
32
+
33
+ times = []
34
+
35
+ 3.times.each do
36
+ album = OpenStruct.new(songs: 100.times.collect { random_song })
37
+
38
+ times << Benchmark.measure do
39
+ puts "================ next!"
40
+ album.extend(AlbumRepresenter).to_json
41
+ end
42
+ end
43
+
44
+ puts times.join("")
45
+
46
+ # 100 songs, 100 attrs
47
+ # 0.050000 0.000000 0.050000 ( 0.093157)
48
+
49
+ ## 100 songs, 1000 attrs
50
+ # 0.470000 0.010000 0.480000 ( 0.483708)
51
+
52
+
53
+ ### without binding cache:
54
+ # 2.790000 0.030000 2.820000 ( 2.820190)
55
+
56
+
57
+
58
+ ### with extend: on Song, with binding cache>
59
+ # 2.490000 0.030000 2.520000 ( 2.517433) 2.4-3.0
60
+ ### without skip?
61
+ # 2.030000 0.020000 2.050000 ( 2.050796) 2.1-2.3
62
+
63
+ ### without :writer
64
+ # 2.270000 0.010000 2.280000 ( 2.284530 1.9-2.2
65
+ ### without :render_filter
66
+ # 2.020000 0.000000 2.020000 ( 2.030234) 1.5-2.0
67
+ ###without default_for and skipable?
68
+ # 1.730000 0.010000 1.740000 ( 1.735597 1.4-1.7
69
+ ### without :serialize
70
+ # 1.780000 0.010000 1.790000 ( 1.786791) 1.4-1.7
71
+ ### using decorator
72
+ # 1.400000 0.030000 1.430000 ( 1.434206) 1.4-1.6
73
+ ### with prepare AFTER representable?
74
+ # 1.330000 0.010000 1.340000 ( 1.335900) 1.1-1.3
75
+
76
+
77
+ # representable 2.0
78
+ # 3.000000 0.020000 3.020000 ( 3.013031) 2.7-3.0
79
+
80
+ # no method missing
81
+ # 2.280000 0.030000 2.310000 ( 2.313522) 2.2-2.5
82
+ # no def_delegator in Definition
83
+ # 2.130000 0.010000 2.140000 ( 2.136115) 1.7-2.1
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ class BindingTest < MiniTest::Spec
4
+ Binding = Representable::Binding
5
+ let (:render_nil_definition) { Representable::Definition.new(:song, :render_nil => true) }
6
+
7
+ describe "#skipable_empty_value?" do
8
+ let (:binding) { Binding.new(render_nil_definition, nil, nil) }
9
+
10
+ # don't skip when present.
11
+ it { binding.skipable_empty_value?("Disconnect, Disconnect").must_equal false }
12
+
13
+ # don't skip when it's nil and render_nil: true
14
+ it { binding.skipable_empty_value?(nil).must_equal false }
15
+
16
+ # skip when nil and :render_nil undefined.
17
+ it { Binding.new(Representable::Definition.new(:song), nil, nil).skipable_empty_value?(nil).must_equal true }
18
+
19
+ # don't skip when nil and :render_nil undefined.
20
+ it { Binding.new(Representable::Definition.new(:song), nil, nil).skipable_empty_value?("Fatal Flu").must_equal false }
21
+ end
22
+
23
+
24
+ describe "#default_for" do
25
+ let (:definition) { Representable::Definition.new(:song, :default => "Insider") }
26
+ let (:binding) { Binding.new(definition, nil, nil) }
27
+
28
+ # return value when value present.
29
+ it { binding.default_for("Black And Blue").must_equal "Black And Blue" }
30
+
31
+ # return false when value false.
32
+ it { binding.default_for(false).must_equal false }
33
+
34
+ # return default when value nil.
35
+ it { binding.default_for(nil).must_equal "Insider" }
36
+
37
+ # return nil when value nil and render_nil: true.
38
+ it { Binding.new(render_nil_definition, nil, nil).default_for(nil).must_equal nil }
39
+
40
+ # return nil when value nil and render_nil: true, even when :default is set" do
41
+ it { Binding.new(Representable::Definition.new(:song, :render_nil => true, :default => "The Quest"), nil, nil).default_for(nil).must_equal nil }
42
+
43
+ # return nil if no :default
44
+ it { Binding.new(Representable::Definition.new(:song), nil, nil).default_for(nil).must_equal nil }
45
+ end
46
+ end
@@ -88,6 +88,11 @@ class DefinitionTest < MiniTest::Spec
88
88
  end
89
89
  end
90
90
 
91
+ # #inspect
92
+ describe "#inspect" do
93
+ it { Definition.new(:songs).inspect.must_equal "#<Representable::Definition ==>songs @options={:parse_filter=>[], :render_filter=>[], :as=>\"songs\"}>" }
94
+ end
95
+
91
96
 
92
97
  describe "generic API" do
93
98
  before do
@@ -186,64 +191,6 @@ class DefinitionTest < MiniTest::Spec
186
191
  end
187
192
 
188
193
 
189
- describe "#skipable_empty_value?" do
190
- # default if skipable_empty_value?
191
- before do
192
- @def = Representable::Definition.new(:song, :render_nil => true)
193
- end
194
-
195
- it "returns false when not nil" do
196
- assert_equal false, @def.skipable_empty_value?("Disconnect, Disconnect")
197
- end
198
-
199
- it "returns false when nil and :render_nil => true" do
200
- assert_equal false, @def.skipable_empty_value?(nil)
201
- end
202
-
203
- it "returns true when nil and :render_nil => false" do
204
- assert_equal true, Representable::Definition.new(:song).skipable_empty_value?(nil)
205
- end
206
-
207
- it "returns false when not nil and :render_nil => false" do
208
- assert_equal false, Representable::Definition.new(:song).skipable_empty_value?("Fatal Flu")
209
- end
210
- end
211
-
212
-
213
- describe "#default_for" do
214
- before do
215
- @def = Representable::Definition.new(:song, :default => "Insider")
216
- end
217
-
218
- it "always returns value when value not nil" do
219
- assert_equal "Black And Blue", @def.default_for("Black And Blue")
220
- end
221
-
222
- it "returns false when value false" do
223
- assert_equal false, @def.default_for(false)
224
- end
225
-
226
- it "returns default when value nil" do
227
- assert_equal "Insider", @def.default_for(nil)
228
- end
229
-
230
- it "returns nil when value nil and :render_nil true" do
231
- @def = Representable::Definition.new(:song, :render_nil => true)
232
- assert_equal nil, @def.default_for(nil)
233
- end
234
-
235
- it "returns nil when value nil and :render_nil true even when :default is set" do
236
- @def = Representable::Definition.new(:song, :render_nil => true, :default => "The Quest")
237
- assert_equal nil, @def.default_for(nil)
238
- end
239
-
240
- it "returns nil if no :default" do
241
- @def = Representable::Definition.new(:song)
242
- assert_equal nil, @def.default_for(nil)
243
- end
244
- end
245
-
246
-
247
194
  describe "#binding" do
248
195
  it "returns true when :binding is set" do
249
196
  assert Representable::Definition.new(:songs, :binding => Object)[:binding]
@@ -40,8 +40,8 @@ class ExecContextTest < MiniTest::Spec
40
40
  }
41
41
  end
42
42
 
43
- it { render(song).must_equal_document({Representable::Hash::PropertyBinding => "name"}) }
44
- it { parse(song, {Representable::Hash::PropertyBinding => "Rebel Fate"}).name.must_equal "Rebel Fate" }
43
+ it { render(song).must_equal_document({Representable::Hash::Binding => "name"}) }
44
+ it { parse(song, {Representable::Hash::Binding => "Rebel Fate"}).name.must_equal "Rebel Fate" }
45
45
  end
46
46
 
47
47
 
@@ -85,8 +85,8 @@ class ExecContextTest < MiniTest::Spec
85
85
  }
86
86
  end
87
87
 
88
- it { render(song).must_equal_document({Representable::Hash::PropertyBinding => "name"}) }
89
- it { parse(song, {Representable::Hash::PropertyBinding => "Rebel Fate"}).name.must_equal "Rebel Fate" }
88
+ it { render(song).must_equal_document({Representable::Hash::Binding => "name"}) }
89
+ it { parse(song, {Representable::Hash::Binding => "Rebel Fate"}).name.must_equal "Rebel Fate" }
90
90
  end
91
91
  end
92
92
  end
@@ -15,7 +15,7 @@ class HashBindingTest < MiniTest::Spec
15
15
  describe "PropertyBinding" do
16
16
  describe "#read" do
17
17
  before do
18
- @property = Representable::Hash::PropertyBinding.new(Representable::Definition.new(:song), nil, nil)
18
+ @property = Representable::Hash::Binding.new(Representable::Definition.new(:song), nil, nil)
19
19
  end
20
20
 
21
21
  it "returns fragment if present" do
@@ -29,61 +29,13 @@ class HashBindingTest < MiniTest::Spec
29
29
  end
30
30
 
31
31
  end
32
-
33
- describe "with plain text" do
34
- before do
35
- @property = Representable::Hash::PropertyBinding.new(Representable::Definition.new(:song), nil, nil)
36
- end
37
-
38
- it "extracts with #read" do
39
- assert_equal "Thinning the Herd", @property.read("song" => "Thinning the Herd")
40
- end
41
-
42
- it "inserts with #write" do
43
- doc = {}
44
- assert_equal("Thinning the Herd", @property.write(doc,"Thinning the Herd"))
45
- assert_equal({"song"=>"Thinning the Herd"}, doc)
46
- end
47
- end
48
-
49
- describe "with an object" do
50
- before do
51
- @property = Representable::Hash::PropertyBinding.new(Representable::Definition.new(:song, :class => SongWithRepresenter), nil, nil)
52
- @doc = {}
53
- end
54
-
55
- it "extracts with #read" do
56
- assert_equal SongWithRepresenter.new("Thinning the Herd"), @property.read("song" => {"name" => "Thinning the Herd"})
57
- end
58
-
59
- it "inserts with #write" do
60
- assert_equal({"name"=>"Thinning the Herd"}, @property.write(@doc, SongWithRepresenter.new("Thinning the Herd")))
61
- assert_equal({"song" => {"name"=>"Thinning the Herd"}}, @doc)
62
- end
63
- end
64
-
65
- describe "with an object and :extend" do
66
- before do
67
- @property = Representable::Hash::PropertyBinding.new(Representable::Definition.new(:song, :class => Song, :extend => SongRepresenter), nil, nil)
68
- @doc = {}
69
- end
70
-
71
- it "extracts with #read" do
72
- assert_equal Song.new("Thinning the Herd"), @property.read("song" => {"name" => "Thinning the Herd"})
73
- end
74
-
75
- it "inserts with #write" do
76
- assert_equal({"name"=>"Thinning the Herd"}, @property.write(@doc, Song.new("Thinning the Herd")))
77
- assert_equal({"song" => {"name"=>"Thinning the Herd"}}, @doc)
78
- end
79
- end
80
32
  end
81
33
 
82
34
 
83
35
  describe "CollectionBinding" do
84
36
  describe "with plain text items" do
85
37
  before do
86
- @property = Representable::Hash::CollectionBinding.new(Representable::Definition.new(:songs, :collection => true), Album.new, nil)
38
+ @property = Representable::Hash::Binding::Collection.new(Representable::Definition.new(:songs, :collection => true), Album.new, nil)
87
39
  end
88
40
 
89
41
  it "extracts with #read" do
@@ -104,7 +56,7 @@ class HashBindingTest < MiniTest::Spec
104
56
  describe "HashBinding" do
105
57
  describe "with plain text items" do
106
58
  before do
107
- @property = Representable::Hash::HashBinding.new(Representable::Definition.new(:songs, :hash => true), nil, nil)
59
+ @property = Representable::Hash::Binding::Hash.new(Representable::Definition.new(:songs, :hash => true), nil, nil)
108
60
  end
109
61
 
110
62
  it "extracts with #read" do
@@ -120,7 +72,7 @@ class HashBindingTest < MiniTest::Spec
120
72
 
121
73
  describe "with objects" do
122
74
  before do
123
- @property = Representable::Hash::HashBinding.new(Representable::Definition.new(:songs, :hash => true, :class => Song, :extend => SongRepresenter), nil, nil)
75
+ @property = Representable::Hash::Binding::Hash.new(Representable::Definition.new(:songs, :hash => true, :class => Song, :extend => SongRepresenter), nil, nil)
124
76
  end
125
77
 
126
78
  it "doesn't change the represented hash in #write" do
@@ -14,7 +14,7 @@ class HashTest < MiniTest::Spec
14
14
 
15
15
 
16
16
  describe "property" do
17
- let (:hash) { hash_representer do property :best_song end }
17
+ let (:hsh) { hash_representer do property :best_song end }
18
18
 
19
19
  let (:album) { Album.new.tap do |album|
20
20
  album.best_song = "Liar"
@@ -22,14 +22,14 @@ class HashTest < MiniTest::Spec
22
22
 
23
23
  describe "#to_hash" do
24
24
  it "renders plain property" do
25
- album.extend(hash).to_hash.must_equal("best_song" => "Liar")
25
+ album.extend(hsh).to_hash.must_equal("best_song" => "Liar")
26
26
  end
27
27
  end
28
28
 
29
29
 
30
30
  describe "#from_hash" do
31
31
  it "parses plain property" do
32
- album.extend(hash).from_hash("best_song" => "This Song Is Recycled").best_song.must_equal "This Song Is Recycled"
32
+ album.extend(hsh).from_hash("best_song" => "This Song Is Recycled").best_song.must_equal "This Song Is Recycled"
33
33
  end
34
34
  end
35
35
 
@@ -95,7 +95,7 @@ class HashTest < MiniTest::Spec
95
95
 
96
96
 
97
97
  describe "collection" do
98
- let (:hash) { hash_representer do collection :songs end }
98
+ let (:hsh) { hash_representer do collection :songs end }
99
99
 
100
100
  let (:album) { Album.new.tap do |album|
101
101
  album.songs = ["Jackhammer", "Terrible Man"]
@@ -104,14 +104,14 @@ class HashTest < MiniTest::Spec
104
104
 
105
105
  describe "#to_hash" do
106
106
  it "renders a block style list per default" do
107
- album.extend(hash).to_hash.must_equal("songs" => ["Jackhammer", "Terrible Man"])
107
+ album.extend(hsh).to_hash.must_equal("songs" => ["Jackhammer", "Terrible Man"])
108
108
  end
109
109
  end
110
110
 
111
111
 
112
112
  describe "#from_hash" do
113
113
  it "parses a block style list" do
114
- album.extend(hash).from_hash("songs" => ["Off Key Melody", "Sinking"]).must_equal Album.new(["Off Key Melody", "Sinking"])
114
+ album.extend(hsh).from_hash("songs" => ["Off Key Melody", "Sinking"]).must_equal Album.new(["Off Key Melody", "Sinking"])
115
115
 
116
116
  end
117
117
  end
@@ -88,21 +88,21 @@ module JsonTest
88
88
 
89
89
  describe "#build_for" do
90
90
  it "returns TextBinding" do
91
- assert_kind_of Representable::Hash::PropertyBinding, Representable::Hash::PropertyBinding.build_for(Def.new(:band), nil, nil)
91
+ assert_kind_of Representable::Hash::Binding, Representable::Hash::Binding.build_for(Def.new(:band), nil, nil)
92
92
  end
93
93
 
94
94
  it "returns HashBinding" do
95
- assert_kind_of Representable::Hash::HashBinding, Representable::Hash::PropertyBinding.build_for(Def.new(:band, :hash => true), nil, nil)
95
+ assert_kind_of Representable::Hash::Binding::Hash, Representable::Hash::Binding.build_for(Def.new(:band, :hash => true), nil, nil)
96
96
  end
97
97
 
98
98
  it "returns CollectionBinding" do
99
- assert_kind_of Representable::Hash::CollectionBinding, Representable::Hash::PropertyBinding.build_for(Def.new(:band, :collection => true), nil, nil)
99
+ assert_kind_of Representable::Hash::Binding::Collection, Representable::Hash::Binding.build_for(Def.new(:band, :collection => true), nil, nil)
100
100
  end
101
101
  end
102
102
 
103
103
  # describe "#representable_bindings_for" do
104
104
  # it "returns bindings for each property" do
105
- # bins = @band.send(:representable_bindings_for, Representable::JSON::PropertyBinding, {})
105
+ # bins = @band.send(:representable_bindings_for, Representable::JSON::Binding, {})
106
106
  # assert_equal 2, bins.size
107
107
  # assert_equal "name", bins.first.name
108
108
  # end
@@ -395,15 +395,15 @@ end
395
395
 
396
396
  describe "parsing" do
397
397
  subject { OpenStruct.new.extend(representer) }
398
- let (:hash) { {"7"=>{"name"=>"Contemplation"}} }
398
+ let (:hsh) { {"7"=>{"name"=>"Contemplation"}} }
399
399
 
400
400
  it "parses incoming hash" do
401
- subject.from_hash("songs"=>hash).songs.must_equal({"7"=>Song.new("Contemplation")})
401
+ subject.from_hash("songs"=>hsh).songs.must_equal({"7"=>Song.new("Contemplation")})
402
402
  end
403
403
 
404
404
  it "doesn't modify the incoming hash" do
405
- subject.from_hash("songs"=> incoming_hash = hash.dup)
406
- hash.must_equal incoming_hash
405
+ subject.from_hash("songs"=> incoming_hash = hsh.dup)
406
+ hsh.must_equal incoming_hash
407
407
  end
408
408
  end
409
409
  end