representable 2.4.0.rc3 → 2.4.0.rc4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +7 -2
  3. data/Rakefile +6 -0
  4. data/lib/representable.rb +21 -17
  5. data/lib/representable/binding.rb +3 -2
  6. data/lib/representable/definition.rb +1 -1
  7. data/lib/representable/deprecations.rb +31 -5
  8. data/lib/representable/deserializer.rb +24 -34
  9. data/lib/representable/hash/collection.rb +9 -2
  10. data/lib/representable/hash_methods.rb +2 -2
  11. data/lib/representable/parse_strategies.rb +6 -7
  12. data/lib/representable/pipeline.rb +12 -1
  13. data/lib/representable/pipeline_factories.rb +9 -2
  14. data/lib/representable/serializer.rb +3 -3
  15. data/lib/representable/version.rb +1 -1
  16. data/test-with-deprecations/as_test.rb +65 -0
  17. data/test-with-deprecations/benchmarking.rb +83 -0
  18. data/test-with-deprecations/binding_test.rb +46 -0
  19. data/test-with-deprecations/blaaaaaaaa_test.rb +69 -0
  20. data/test-with-deprecations/cached_test.rb +147 -0
  21. data/test-with-deprecations/class_test.rb +119 -0
  22. data/test-with-deprecations/coercion_test.rb +52 -0
  23. data/test-with-deprecations/config/inherit_test.rb +135 -0
  24. data/test-with-deprecations/config_test.rb +122 -0
  25. data/test-with-deprecations/decorator_scope_test.rb +28 -0
  26. data/test-with-deprecations/decorator_test.rb +96 -0
  27. data/test-with-deprecations/default_test.rb +34 -0
  28. data/test-with-deprecations/defaults_options_test.rb +93 -0
  29. data/test-with-deprecations/definition_test.rb +264 -0
  30. data/test-with-deprecations/example.rb +310 -0
  31. data/test-with-deprecations/examples/object.rb +31 -0
  32. data/test-with-deprecations/exec_context_test.rb +93 -0
  33. data/test-with-deprecations/features_test.rb +70 -0
  34. data/test-with-deprecations/filter_test.rb +57 -0
  35. data/test-with-deprecations/for_collection_test.rb +74 -0
  36. data/test-with-deprecations/generic_test.rb +116 -0
  37. data/test-with-deprecations/getter_setter_test.rb +21 -0
  38. data/test-with-deprecations/hash_bindings_test.rb +87 -0
  39. data/test-with-deprecations/hash_test.rb +160 -0
  40. data/test-with-deprecations/heritage_test.rb +62 -0
  41. data/test-with-deprecations/if_test.rb +79 -0
  42. data/test-with-deprecations/include_exclude_test.rb +88 -0
  43. data/test-with-deprecations/inherit_test.rb +159 -0
  44. data/test-with-deprecations/inline_test.rb +272 -0
  45. data/test-with-deprecations/instance_test.rb +266 -0
  46. data/test-with-deprecations/is_representable_test.rb +77 -0
  47. data/test-with-deprecations/json_test.rb +355 -0
  48. data/test-with-deprecations/lonely_test.rb +239 -0
  49. data/test-with-deprecations/mongoid_test.rb +31 -0
  50. data/test-with-deprecations/nested_test.rb +115 -0
  51. data/test-with-deprecations/object_test.rb +60 -0
  52. data/{test/---deserialize-pipeline_test.rb → test-with-deprecations/parse_pipeline_test.rb} +29 -2
  53. data/test-with-deprecations/parse_strategy_test.rb +279 -0
  54. data/{test → test-with-deprecations}/pass_options_test.rb +0 -0
  55. data/test-with-deprecations/pipeline_test.rb +277 -0
  56. data/test-with-deprecations/populator_test.rb +105 -0
  57. data/test-with-deprecations/prepare_test.rb +67 -0
  58. data/test-with-deprecations/private_options_test.rb +18 -0
  59. data/test-with-deprecations/reader_writer_test.rb +19 -0
  60. data/test-with-deprecations/realistic_benchmark.rb +115 -0
  61. data/test-with-deprecations/render_nil_test.rb +21 -0
  62. data/test-with-deprecations/represent_test.rb +88 -0
  63. data/test-with-deprecations/representable_test.rb +511 -0
  64. data/test-with-deprecations/schema_test.rb +148 -0
  65. data/test-with-deprecations/serialize_deserialize_test.rb +33 -0
  66. data/test-with-deprecations/skip_test.rb +81 -0
  67. data/test-with-deprecations/stringify_hash_test.rb +41 -0
  68. data/test-with-deprecations/test_helper.rb +135 -0
  69. data/test-with-deprecations/test_helper_test.rb +25 -0
  70. data/test-with-deprecations/uncategorized_test.rb +67 -0
  71. data/test-with-deprecations/user_options_test.rb +15 -0
  72. data/test-with-deprecations/wrap_test.rb +152 -0
  73. data/test-with-deprecations/xml_bindings_test.rb +62 -0
  74. data/test-with-deprecations/xml_test.rb +503 -0
  75. data/test-with-deprecations/yaml_test.rb +162 -0
  76. data/test/as_test.rb +3 -3
  77. data/test/cached_test.rb +2 -2
  78. data/test/class_test.rb +5 -5
  79. data/test/exec_context_test.rb +2 -2
  80. data/test/filter_test.rb +1 -1
  81. data/test/getter_setter_test.rb +4 -4
  82. data/test/if_test.rb +2 -2
  83. data/test/include_exclude_test.rb +88 -0
  84. data/test/instance_test.rb +15 -15
  85. data/test/lonely_test.rb +18 -2
  86. data/test/object_test.rb +4 -4
  87. data/test/parse_pipeline_test.rb +64 -0
  88. data/test/parse_strategy_test.rb +3 -3
  89. data/test/pipeline_test.rb +8 -12
  90. data/test/prepare_test.rb +2 -3
  91. data/test/reader_writer_test.rb +3 -3
  92. data/test/representable_test.rb +12 -48
  93. data/test/serialize_deserialize_test.rb +9 -9
  94. data/test/skip_test.rb +11 -11
  95. data/test/test_helper.rb +2 -0
  96. data/test/uncategorized_test.rb +10 -10
  97. data/test/user_options_test.rb +15 -0
  98. data/test/wrap_test.rb +1 -1
  99. metadata +65 -4
@@ -0,0 +1,52 @@
1
+ require 'test_helper'
2
+ require 'representable/coercion'
3
+
4
+ class VirtusCoercionTest < MiniTest::Spec
5
+ representer! do
6
+ include Representable::Coercion
7
+
8
+ property :title # no coercion.
9
+ property :length, :type => Float
10
+
11
+ property :band, :class => OpenStruct do
12
+ property :founded, :type => Integer
13
+ end
14
+
15
+ collection :songs, :class => OpenStruct do
16
+ property :ok, :type => Virtus::Attribute::Boolean
17
+ end
18
+ end
19
+
20
+ let (:album) { OpenStruct.new(:title => "Dire Straits", :length => 41.34,
21
+ :band => OpenStruct.new(:founded => "1977"),
22
+ :songs => [OpenStruct.new(:ok => 1), OpenStruct.new(:ok => 0)]) }
23
+
24
+ it { album.extend(representer).to_hash.must_equal({"title"=>"Dire Straits", "length"=>41.34, "band"=>{"founded"=>1977}, "songs"=>[{"ok"=>true}, {"ok"=>false}]}) }
25
+
26
+ it {
27
+ album = OpenStruct.new
28
+ album.extend(representer)
29
+ album.from_hash({"title"=>"Dire Straits", "length"=>"41.34", "band"=>{"founded"=>"1977"}, "songs"=>[{"ok"=>1}, {"ok"=>0}]})
30
+
31
+ # it
32
+ album.length.must_equal 41.34
33
+ album.band.founded.must_equal 1977
34
+ album.songs[0].ok.must_equal true
35
+ }
36
+
37
+
38
+ describe "with user :parse_filter and :render_filter" do
39
+ representer! do
40
+ include Representable::Coercion
41
+
42
+ property :length, :type => Float,
43
+ :parse_filter => lambda { |input, options| "#{input}.1" }, # happens BEFORE coercer.
44
+ :render_filter => lambda { |fragment,*| "#{fragment}.1" }
45
+ end
46
+
47
+ # user's :parse_filter(s) are run before coercion.
48
+ it { OpenStruct.new.extend(representer).from_hash("length"=>"1").length.must_equal 1.1 }
49
+ # user's :render_filter(s) are run before coercion.
50
+ it { OpenStruct.new(:length=>1).extend(representer).to_hash.must_equal({"length" => 1.1}) }
51
+ end
52
+ end
@@ -0,0 +1,135 @@
1
+ require 'test_helper'
2
+
3
+ # tests defining representers in modules, decorators and classes and the inheritance when combined.
4
+
5
+ class ConfigInheritTest < MiniTest::Spec
6
+ def assert_cloned(child, parent, property)
7
+ child_def = child.representable_attrs.get(property)
8
+ parent_def = parent.representable_attrs.get(property)
9
+
10
+ child_def.merge!(:alias => property)
11
+
12
+ child_def[:alias].wont_equal parent_def[:alias]
13
+ child_def.object_id.wont_equal parent_def.object_id
14
+ end
15
+ # class Object
16
+
17
+ # end
18
+ module GenreModule
19
+ include Representable::Hash
20
+ property :genre
21
+ end
22
+
23
+
24
+ # in Decorator ------------------------------------------------
25
+ class Decorator < Representable::Decorator
26
+ include Representable::Hash
27
+ property :title
28
+ property :artist do
29
+ property :id
30
+ end
31
+ end
32
+
33
+ it { Decorator.definitions.keys.must_equal ["title", "artist"] }
34
+
35
+ # in inheriting Decorator
36
+
37
+ class InheritingDecorator < Decorator
38
+ property :location
39
+ end
40
+
41
+ it { InheritingDecorator.definitions.keys.must_equal ["title", "artist", "location"] }
42
+ it { assert_cloned(InheritingDecorator, Decorator, "title") }
43
+ it do
44
+ InheritingDecorator.representable_attrs.get(:artist).representer_module.object_id.wont_equal Decorator.representable_attrs.get(:artist).representer_module.object_id
45
+ end
46
+
47
+ # in inheriting and including Decorator
48
+
49
+ class InheritingAndIncludingDecorator < Decorator
50
+ include GenreModule
51
+ property :location
52
+ end
53
+
54
+ it { InheritingAndIncludingDecorator.definitions.keys.must_equal ["title", "artist", "genre", "location"] }
55
+ it { assert_cloned(InheritingAndIncludingDecorator, GenreModule, :genre) }
56
+
57
+
58
+ # in module ---------------------------------------------------
59
+ module Module
60
+ include Representable
61
+ property :title
62
+ end
63
+
64
+ it { Module.definitions.keys.must_equal ["title"] }
65
+
66
+
67
+ # in module including module
68
+ module SubModule
69
+ include Representable
70
+ include Module
71
+
72
+ property :location
73
+ end
74
+
75
+ it { SubModule.definitions.keys.must_equal ["title", "location"] }
76
+ it { assert_cloned(SubModule, Module, :title) }
77
+
78
+ # including preserves order
79
+ module IncludingModule
80
+ include Representable
81
+ property :genre
82
+ include Module
83
+
84
+ property :location
85
+ end
86
+
87
+ it { IncludingModule.definitions.keys.must_equal ["genre", "title", "location"] }
88
+
89
+
90
+ # included in class -------------------------------------------
91
+ class Class
92
+ include Representable
93
+ include IncludingModule
94
+ end
95
+
96
+ it { Class.definitions.keys.must_equal ["genre", "title", "location"] }
97
+ it { assert_cloned(Class, IncludingModule, :title) }
98
+ it { assert_cloned(Class, IncludingModule, :location) }
99
+ it { assert_cloned(Class, IncludingModule, :genre) }
100
+
101
+ # included in class with order
102
+ class DefiningClass
103
+ include Representable
104
+ property :street_cred
105
+ include IncludingModule
106
+ end
107
+
108
+ it { DefiningClass.definitions.keys.must_equal ["street_cred", "genre", "title", "location"] }
109
+
110
+ # in class
111
+ class RepresenterClass
112
+ include Representable
113
+ property :title
114
+ end
115
+
116
+ it { RepresenterClass.definitions.keys.must_equal ["title"] }
117
+
118
+
119
+ # in inheriting class
120
+ class InheritingClass < RepresenterClass
121
+ include Representable
122
+ property :location
123
+ end
124
+
125
+ it { InheritingClass.definitions.keys.must_equal ["title", "location"] }
126
+ it { assert_cloned(InheritingClass, RepresenterClass, :title) }
127
+
128
+ # in inheriting class and including
129
+ class InheritingAndIncludingClass < RepresenterClass
130
+ property :location
131
+ include GenreModule
132
+ end
133
+
134
+ it { InheritingAndIncludingClass.definitions.keys.must_equal ["title", "location", "genre"] }
135
+ end
@@ -0,0 +1,122 @@
1
+ require 'test_helper'
2
+
3
+ class ConfigTest < MiniTest::Spec
4
+ subject { Representable::Config.new(Representable::Definition) }
5
+ PunkRock = Class.new
6
+ Definition = Representable::Definition
7
+
8
+ describe "wrapping" do
9
+ it "returns false per default" do
10
+ assert_equal nil, subject.wrap_for("Punk", nil)
11
+ end
12
+
13
+ # it "infers a printable class name if set to true" do
14
+ # subject.wrap = true
15
+ # assert_equal "punk_rock", subject.wrap_for(PunkRock, nil)
16
+ # end
17
+
18
+ # it "can be set explicitely" do
19
+ # subject.wrap = "Descendents"
20
+ # assert_equal "Descendents", subject.wrap_for(PunkRock, nil)
21
+ # end
22
+ end
23
+
24
+ describe "#[]" do
25
+ # does return nil for non-existent
26
+ it { subject[:hello].must_equal nil }
27
+ end
28
+
29
+ # describe "#[]" do
30
+ # before { subject.add(:title, {:me => true}) }
31
+
32
+ # it { subject[:unknown].must_equal nil }
33
+ # it { subject.get(:title)[:me].must_equal true }
34
+ # it { subject["title"][:me].must_equal true }
35
+ # end
36
+
37
+ # []=
38
+ # []=(... inherit: true)
39
+ # forwarded to Config#definitions
40
+ # that goes to ConfigDefinitionsTest
41
+ describe "#add" do
42
+ describe "returns" do
43
+ it do
44
+ # #add returns Definition.`
45
+ subject = Representable::Config.new(Representable::Definition).add(:title, {:me => true})
46
+
47
+ subject.must_be_kind_of Representable::Definition
48
+ subject[:me].must_equal true
49
+ end
50
+ end
51
+
52
+ before { subject.add(:title, {:me => true}) }
53
+
54
+ # must be kind of Definition
55
+ it { subject.size.must_equal 1 }
56
+ it { subject.get(:title).name.must_equal "title" }
57
+ it { subject.get(:title)[:me].must_equal true }
58
+
59
+ # this is actually tested in context in inherit_test.
60
+ it "overrides former definition" do
61
+ subject.add(:title, {:peer => Module})
62
+ subject.get(:title)[:me].must_equal nil
63
+ subject.get(:title)[:peer].must_equal Module
64
+ end
65
+
66
+ describe "inherit: true" do
67
+ before {
68
+ subject.add(:title, {:me => true})
69
+ subject.add(:title, {:peer => Module, :inherit => true})
70
+ }
71
+
72
+ it { subject.get(:title)[:me].must_equal true }
73
+ it { subject.get(:title)[:peer].must_equal Module }
74
+ end
75
+ end
76
+
77
+
78
+ describe "#remove" do
79
+ subject { Representable::Config.new(Representable::Definition) }
80
+
81
+ it do
82
+ subject.add(:title, {:me => true})
83
+ subject.add(:genre, {})
84
+ subject.get(:genre).must_be_kind_of Representable::Definition
85
+
86
+ subject.remove(:genre)
87
+ subject.get(:genre).must_equal nil
88
+ end
89
+ end
90
+
91
+
92
+ describe "#each" do
93
+ before { subject.add(:title, {:me => true}) }
94
+
95
+ it "what" do
96
+ definitions = []
97
+ subject.each { |dfn| definitions << dfn }
98
+ definitions.size.must_equal 1
99
+ definitions[0][:me].must_equal true
100
+ end
101
+ end
102
+
103
+ describe "#options" do
104
+ it { subject.options.must_equal({}) }
105
+ it do
106
+ subject.options[:namespacing] = true
107
+ subject.options[:namespacing].must_equal true
108
+ end
109
+ end
110
+
111
+ describe "#get" do
112
+ subject { Representable::Config.new(Representable::Definition) }
113
+
114
+ it do
115
+ title = subject.add(:title, {})
116
+ length = subject.add(:length, {})
117
+
118
+ subject.get(:title).must_equal title
119
+ subject.get(:length).must_equal length
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,28 @@
1
+ require 'test_helper'
2
+
3
+ # TODO: remove in 2.0.
4
+ class DecoratorScopeTest < MiniTest::Spec
5
+ representer! do
6
+ property :title, :getter => lambda { |*| title_from_representer }, :decorator_scope => true
7
+ end
8
+
9
+ let (:representer_with_method) {
10
+ Module.new do
11
+ include Representable::Hash
12
+ property :title, :decorator_scope => true
13
+ def title; "Crystal Planet"; end
14
+ end
15
+ }
16
+
17
+ it "executes lambdas in represented context" do
18
+ Class.new do
19
+ def title_from_representer
20
+ "Sounds Of Silence"
21
+ end
22
+ end.new.extend(representer).to_hash.must_equal({"title"=>"Sounds Of Silence"})
23
+ end
24
+
25
+ it "executes method in represented context" do
26
+ Object.new.extend(representer_with_method).to_hash.must_equal({"title"=>"Crystal Planet"})
27
+ end
28
+ end
@@ -0,0 +1,96 @@
1
+ require 'test_helper'
2
+
3
+ class DecoratorTest < MiniTest::Spec
4
+ class SongRepresentation < Representable::Decorator
5
+ include Representable::JSON
6
+ property :name
7
+ end
8
+
9
+ class AlbumRepresentation < Representable::Decorator
10
+ include Representable::JSON
11
+
12
+ collection :songs, :class => Song, :extend => SongRepresentation
13
+ end
14
+
15
+ let (:song) { Song.new("Mama, I'm Coming Home") }
16
+ let (:album) { Album.new([song]) }
17
+
18
+ describe "inheritance" do
19
+ let (:inherited_decorator) do
20
+ Class.new(AlbumRepresentation) do
21
+ property :best_song
22
+ end.new(Album.new([song], "Stand Up"))
23
+ end
24
+
25
+ it { inherited_decorator.to_hash.must_equal({"songs"=>[{"name"=>"Mama, I'm Coming Home"}], "best_song"=>"Stand Up"}) }
26
+ end
27
+
28
+ let (:decorator) { AlbumRepresentation.new(album) }
29
+
30
+ it "renders" do
31
+ decorator.to_hash.must_equal({"songs"=>[{"name"=>"Mama, I'm Coming Home"}]})
32
+ album.wont_respond_to :to_hash
33
+ song.wont_respond_to :to_hash # DISCUSS: weak test, how to assert blank slate?
34
+ # no @representable_attrs in decorated objects
35
+ song.instance_variable_get(:@representable_attrs).must_equal nil
36
+ end
37
+
38
+ describe "#from_hash" do
39
+ it "returns represented" do
40
+ decorator.from_hash({"songs"=>[{"name"=>"Mama, I'm Coming Home"}]}).must_equal album
41
+ end
42
+
43
+ it "parses" do
44
+ decorator.from_hash({"songs"=>[{"name"=>"Atomic Garden"}]})
45
+ album.songs.first.must_be_kind_of Song
46
+ album.songs.must_equal [Song.new("Atomic Garden")]
47
+ album.wont_respond_to :to_hash
48
+ song.wont_respond_to :to_hash # DISCUSS: weak test, how to assert blank slate?
49
+ end
50
+ end
51
+
52
+ describe "#decorated" do
53
+ it "is aliased to #represented" do
54
+ AlbumRepresentation.prepare(album).decorated.must_equal album
55
+ end
56
+ end
57
+
58
+
59
+ describe "inline decorators" do
60
+ representer!(decorator: true) do
61
+ collection :songs, :class => Song do
62
+ property :name
63
+ end
64
+ end
65
+
66
+ it "does not pollute represented" do
67
+ representer.new(album).from_hash({"songs"=>[{"name"=>"Atomic Garden"}]})
68
+
69
+ # no @representable_attrs in decorated objects
70
+ song.instance_variable_get(:@representable_attrs).must_equal nil
71
+ album.instance_variable_get(:@representable_attrs).must_equal nil
72
+ end
73
+ end
74
+ end
75
+
76
+ require "uber/inheritable_attr"
77
+ class InheritanceWithDecoratorTest < MiniTest::Spec
78
+ class Twin
79
+ extend Uber::InheritableAttr
80
+ inheritable_attr :representer_class
81
+ self.representer_class = Class.new(Representable::Decorator){ include Representable::Hash }
82
+ end
83
+
84
+ class Album < Twin
85
+ representer_class.property :title # Twin.representer_class.clone
86
+ end
87
+
88
+ class Song < Twin # Twin.representer_class.clone
89
+ end
90
+
91
+ it do
92
+ Twin.representer_class.definitions.size.must_equal 0
93
+ Album.representer_class.definitions.size.must_equal 1
94
+ Song.representer_class.definitions.size.must_equal 0
95
+ end
96
+ end
@@ -0,0 +1,34 @@
1
+ require "test_helper"
2
+
3
+ class DefaultTest < MiniTest::Spec
4
+ Song = Struct.new(:id, :title)
5
+
6
+ representer! do
7
+ property :id
8
+ property :title, default: "Huber Breeze" #->(options) { options[:default] }
9
+ end
10
+
11
+ describe "#from_hash" do
12
+ let (:song) { Song.new.extend(representer) }
13
+
14
+ it { song.from_hash({}).must_equal Song.new(nil, "Huber Breeze") }
15
+ # default doesn't apply when empty string.
16
+ it { song.from_hash({"title"=>""}).must_equal Song.new(nil, "") }
17
+ it { song.from_hash({"title"=>nil}).must_equal Song.new(nil, nil) }
18
+ it { song.from_hash({"title"=>"Blindfold"}).must_equal Song.new(nil, "Blindfold") }
19
+ end
20
+
21
+ describe "#to_json" do
22
+ it "uses :default when not available from object" do
23
+ Song.new.extend(representer).to_hash.must_equal({"title"=>"Huber Breeze"})
24
+ end
25
+
26
+ it "uses value from represented object when present" do
27
+ Song.new(nil, "After The War").extend(representer).to_hash.must_equal({"title"=>"After The War"})
28
+ end
29
+
30
+ it "uses value from represented object when emtpy string" do
31
+ Song.new(nil, "").extend(representer).to_hash.must_equal({"title"=>""})
32
+ end
33
+ end
34
+ end