representable 2.4.0.rc3 → 2.4.0.rc4

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