representable 2.0.4 → 2.1.0

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.
@@ -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