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,31 @@
1
+ require "test_helper"
2
+
3
+ require "ostruct"
4
+ require "pp"
5
+
6
+
7
+ source = OpenStruct.new(name: "30 Years Live", songs: [
8
+ OpenStruct.new(id: 1, title: "Dear Beloved"), OpenStruct.new(id: 2, title: "Fuck Armageddon")])
9
+
10
+ pp source
11
+
12
+ require "representable/object"
13
+
14
+ class AlbumRepresenter < Representable::Decorator
15
+ include Representable::Object
16
+
17
+ property :name
18
+ collection :songs, instance: ->(fragment, *) { Song.new } do
19
+ property :title
20
+ end
21
+ end
22
+
23
+
24
+ Album = Struct.new(:name, :songs)
25
+ Song = Struct.new(:title)
26
+
27
+ target = Album.new
28
+
29
+ AlbumRepresenter.new(target).from_object(source)
30
+
31
+ pp target
@@ -0,0 +1,93 @@
1
+ require 'test_helper'
2
+
3
+ class ExecContextTest < MiniTest::Spec
4
+ for_formats(
5
+ :hash => [Representable::Hash, {Song => "Rebel Fate"}, {Song=>"Timing"}],
6
+ # :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>"],
7
+ # :yaml => [Representable::YAML, "---\nsong:\n name: Alive\n", "---\nsong:\n name: You've Taken Everything\n"],
8
+ ) do |format, mod, input, output|
9
+
10
+ let (:song) { representer.prepare(Song.new("Timing")) }
11
+ let (:format) { format }
12
+
13
+
14
+ describe "exec_context: nil" do
15
+ representer!(:module => mod) do
16
+ property :name, :as => lambda { |*| self.class }
17
+ end
18
+
19
+ it { render(song).must_equal_document output }
20
+ it { parse(song, input).name.must_equal "Rebel Fate" }
21
+ end
22
+
23
+
24
+ describe "exec_context: :decorator" do
25
+ representer!(:module => mod) do
26
+ property :name, :as => lambda { |*| self.class }, :exec_context => :decorator
27
+ end
28
+
29
+ it { render(song).must_equal_document output }
30
+ it { parse(song, input).name.must_equal "Rebel Fate" }
31
+ end
32
+
33
+
34
+ describe "exec_context: :binding" do
35
+ representer!(:module => mod) do
36
+ property :name,
37
+ :as => lambda { |*| self.class }, # to actually test
38
+ :exec_context => :binding,
39
+ :setter => lambda { |v, *args| represented.name = v # to make parsing work.
40
+ }
41
+ end
42
+
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
+ end
46
+
47
+
48
+ describe "Decorator" do
49
+ # DISCUSS: do we need this test?
50
+ describe "exec_context: nil" do
51
+ representer!(:module => mod, :decorator => true) do
52
+ property :name, :as => lambda { |*| self.class }
53
+ end
54
+
55
+ it { render(song).must_equal_document output }
56
+ it { parse(song, input).name.must_equal "Rebel Fate" }
57
+ end
58
+
59
+
60
+ describe "exec_context: :decorator" do # this tests if lambdas are run in the right context, if methods are called in the right context and if we can access the represented object.
61
+ representer!(:module => mod, :decorator => true) do
62
+ property :name, :as => lambda { |*| self.class.superclass }, :exec_context => :decorator
63
+
64
+ define_method :name do # def in Decorator class.
65
+ "Timebomb"
66
+ end
67
+
68
+ define_method :"name=" do |v| # def in Decorator class.
69
+ represented.name = v
70
+ end
71
+ end
72
+
73
+ it { render(song).must_equal_document({Representable::Decorator=>"Timebomb"}) }
74
+ it { parse(song, {Representable::Decorator=>"Listless"}).name.must_equal "Listless" }
75
+ end
76
+
77
+
78
+ # DISCUSS: do we need this test?
79
+ describe "exec_context: :binding" do
80
+ representer!(:module => mod, :decorator => true) do
81
+ property :name,
82
+ :as => lambda { |*| self.class }, # to actually test
83
+ :exec_context => :binding,
84
+ :setter => lambda { |v, *args| represented.name = v # to make parsing work.
85
+ }
86
+ end
87
+
88
+ it { render(song).must_equal_document({Representable::Hash::Binding => "name"}) }
89
+ it("xxx") { parse(song, {Representable::Hash::Binding => "Rebel Fate"}).name.must_equal "Rebel Fate" }
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,70 @@
1
+ require 'test_helper'
2
+
3
+ class FeaturesTest < MiniTest::Spec
4
+ module Title
5
+ def title; "Is It A Lie"; end
6
+ end
7
+ module Length
8
+ def length; "2:31"; end
9
+ end
10
+
11
+ definition = lambda {
12
+ feature Title
13
+ feature Length
14
+
15
+ # exec_context: :decorator, so the readers are called on the Decorator instance (which gets readers from features!).
16
+ property :title, exec_context: :decorator
17
+ property :length, exec_context: :decorator
18
+ property :details do
19
+ property :title, exec_context: :decorator
20
+ end
21
+ }
22
+
23
+ let (:song) { OpenStruct.new(:details => Object.new) }
24
+
25
+ describe "Module" do
26
+ representer! do
27
+ instance_exec &definition
28
+ end
29
+
30
+ it { song.extend(representer).to_hash.must_equal({"title"=>"Is It A Lie", "length"=>"2:31", "details"=>{"title"=>"Is It A Lie"}}) }
31
+ end
32
+
33
+
34
+ describe "Decorator" do
35
+ representer!(:decorator => true) do
36
+ instance_exec &definition
37
+ end
38
+
39
+ it { representer.new(song).to_hash.must_equal({"title"=>"Is It A Lie", "length"=>"2:31", "details"=>{"title"=>"Is It A Lie"}}) }
40
+ end
41
+ end
42
+
43
+ class FeatureInclusionOrderTest < MiniTest::Spec
44
+ module Title
45
+ def title
46
+ "I was first!"
47
+ end
48
+ end
49
+
50
+ module OverridingTitle
51
+ def title
52
+ "I am number two, " + super
53
+ end
54
+ end
55
+
56
+ representer!(decorator: true) do
57
+ feature Title
58
+ feature OverridingTitle
59
+
60
+ property :title, exec_context: :decorator
61
+
62
+ property :song do
63
+ property :title, exec_context: :decorator
64
+ end
65
+ end
66
+
67
+ it do
68
+ representer.new(OpenStruct.new(song: Object)).to_hash.must_equal({"title"=>"I am number two, I was first!", "song"=>{"title"=>"I am number two, I was first!"}})
69
+ end
70
+ end
@@ -0,0 +1,57 @@
1
+ require 'test_helper'
2
+
3
+ class FilterPipelineTest < MiniTest::Spec
4
+ let (:block1) { lambda { |input, options| "1: #{input}" } }
5
+ let (:block2) { lambda { |input, options| "2: #{input}" } }
6
+
7
+ subject { Representable::Pipeline[block1, block2] }
8
+
9
+ it { subject.call("Horowitz", {}).must_equal "2: 1: Horowitz" }
10
+ end
11
+
12
+
13
+ class FilterTest < MiniTest::Spec
14
+ representer! do
15
+ property :title
16
+
17
+ property :track,
18
+ :parse_filter => lambda { |input, options| "#{input.downcase},#{options[:doc]}" },
19
+ :render_filter => lambda { |val, options| "#{val.upcase},#{options[:doc]},#{options[:options][:user_options]}" }
20
+ end
21
+
22
+ # gets doc and options.
23
+ it {
24
+ song = OpenStruct.new.extend(representer).from_hash("title" => "VULCAN EARS", "track" => "Nine")
25
+ song.title.must_equal "VULCAN EARS"
26
+ song.track.must_equal "nine,{\"title\"=>\"VULCAN EARS\", \"track\"=>\"Nine\"}"
27
+ }
28
+
29
+ it { OpenStruct.new("title" => "vulcan ears", "track" => "Nine").extend(representer).to_hash.must_equal( {"title"=>"vulcan ears", "track"=>"NINE,{\"title\"=>\"vulcan ears\"},{}"}) }
30
+
31
+
32
+ describe "#parse_filter" do
33
+ representer! do
34
+ property :track,
35
+ :parse_filter => [
36
+ lambda { |input, options| "#{input}-1" },
37
+ lambda { |input, options| "#{input}-2" }],
38
+ :render_filter => [
39
+ lambda { |val, options| "#{val}-1" },
40
+ lambda { |val, options| "#{val}-2" }]
41
+ end
42
+
43
+ # order matters.
44
+ it { OpenStruct.new.extend(representer).from_hash("track" => "Nine").track.must_equal "Nine-1-2" }
45
+ it { OpenStruct.new("track" => "Nine").extend(representer).to_hash.must_equal({"track"=>"Nine-1-2"}) }
46
+ end
47
+ end
48
+
49
+
50
+ # class RenderFilterTest < MiniTest::Spec
51
+ # representer! do
52
+ # property :track, :render_filter => [lambda { |val, options| "#{val}-1" } ]
53
+ # property :track, :render_filter => [lambda { |val, options| "#{val}-2" } ], :inherit => true
54
+ # end
55
+
56
+ # it { OpenStruct.new("track" => "Nine").extend(representer).to_hash.must_equal({"track"=>"Nine-1-2"}) }
57
+ # end
@@ -0,0 +1,74 @@
1
+ require 'test_helper'
2
+
3
+ class ForCollectionTest < MiniTest::Spec
4
+ module SongRepresenter
5
+ include Representable::JSON
6
+
7
+ property :name
8
+ end
9
+
10
+ let (:songs) { [Song.new("Days Go By"), Song.new("Can't Take Them All")] }
11
+ let (:json) { "[{\"name\":\"Days Go By\"},{\"name\":\"Can't Take Them All\"}]" }
12
+
13
+
14
+ # Module.for_collection
15
+ # Decorator.for_collection
16
+ for_formats(
17
+ :hash => [Representable::Hash, out=[{"name" => "Days Go By"}, {"name"=>"Can't Take Them All"}], out],
18
+ :json => [Representable::JSON, out="[{\"name\":\"Days Go By\"},{\"name\":\"Can't Take Them All\"}]", out],
19
+ # :xml => [Representable::XML, out="<a><song></song><song></song></a>", out]
20
+ ) do |format, mod, output, input|
21
+
22
+ describe "Module::for_collection [#{format}]" do
23
+ let (:format) { format }
24
+
25
+ let (:representer) {
26
+ Module.new do
27
+ include mod
28
+ property :name#, :as => :title
29
+
30
+ collection_representer :class => Song
31
+
32
+ # self.representation_wrap = :songs if format == :xml
33
+ end
34
+ }
35
+
36
+ it { render(songs.extend(representer.for_collection)).must_equal_document output }
37
+ it { render(representer.for_collection.prepare(songs)).must_equal_document output }
38
+ # parsing needs the class set, at least
39
+ it { parse([].extend(representer.for_collection), input).must_equal songs }
40
+ end
41
+
42
+ describe "Module::for_collection without configuration [#{format}]" do
43
+ let (:format) { format }
44
+
45
+ let (:representer) {
46
+ Module.new do
47
+ include mod
48
+ property :name
49
+ end
50
+ }
51
+
52
+ # rendering works out of the box, no config necessary
53
+ it { render(songs.extend(representer.for_collection)).must_equal_document output }
54
+ end
55
+
56
+
57
+ describe "Decorator::for_collection [#{format}]" do
58
+ let (:format) { format }
59
+ let (:representer) {
60
+ Class.new(Representable::Decorator) do
61
+ include mod
62
+ property :name
63
+
64
+ collection_representer :class => Song
65
+ end
66
+ }
67
+
68
+ it { render(representer.for_collection.new(songs)).must_equal_document output }
69
+ it { parse(representer.for_collection.new([]), input).must_equal songs }
70
+ end
71
+ end
72
+
73
+ # with module including module
74
+ end
@@ -0,0 +1,116 @@
1
+ require 'test_helper'
2
+
3
+ class GenericTest < MiniTest::Spec # TODO: rename/restructure to CollectionTest.
4
+ let (:new_album) { OpenStruct.new.extend(representer) }
5
+ let (:album) { OpenStruct.new(:songs => ["Fuck Armageddon"]).extend(representer) }
6
+ let (:song) { OpenStruct.new(:title => "Resist Stance") }
7
+ let (:song_representer) { Module.new do include Representable::Hash; property :title end }
8
+
9
+
10
+ describe "::collection" do
11
+ representer! do
12
+ collection :songs
13
+ end
14
+
15
+ it "doesn't initialize property" do
16
+ new_album.from_hash({})
17
+ new_album.songs.must_equal nil
18
+ end
19
+
20
+ it "leaves properties untouched" do
21
+ album.from_hash({})
22
+ # TODO: test property.
23
+ album.songs.must_equal ["Fuck Armageddon"] # when the collection is not present in the incoming hash, this propery stays untouched.
24
+ end
25
+
26
+
27
+ # when collection is nil, it doesn't get rendered:
28
+ for_formats(
29
+ :hash => [Representable::Hash, {}],
30
+ :xml => [Representable::XML, "<open_struct></open_struct>"],
31
+ :yaml => [Representable::YAML, "--- {}\n"], # FIXME: this doesn't look right.
32
+ ) do |format, mod, output, input|
33
+
34
+ describe "nil collections" do
35
+ let (:format) { format }
36
+
37
+ representer!(:module => mod) do
38
+ collection :songs
39
+ self.representation_wrap = :album if format == :xml
40
+ end
41
+
42
+ let (:album) { Album.new.extend(representer) }
43
+
44
+ it "doesn't render collection in #{format}" do
45
+ render(album).must_equal_document output
46
+ end
47
+ end
48
+ end
49
+
50
+ # when collection is set but empty, render the empty collection.
51
+ for_formats(
52
+ :hash => [Representable::Hash, {"songs" => []}],
53
+ #:xml => [Representable::XML, "<open_struct><songs/></open_struct>"],
54
+ :yaml => [Representable::YAML, "---\nsongs: []\n"],
55
+ ) do |format, mod, output, input|
56
+
57
+ describe "empty collections" do
58
+ let (:format) { format }
59
+
60
+ representer!(:module => mod) do
61
+ collection :songs
62
+ self.representation_wrap = :album if format == :xml
63
+ end
64
+
65
+ let (:album) { OpenStruct.new(:songs => []).extend(representer) }
66
+
67
+ it "renders empty collection in #{format}" do
68
+ render(album).must_equal_document output
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+ # when collection is [], suppress rendering when render_empty: false.
75
+ for_formats(
76
+ :hash => [Representable::Hash, {}],
77
+ #:xml => [Representable::XML, "<open_struct><songs/></open_struct>"],
78
+ :yaml => [Representable::YAML, "--- {}\n"],
79
+ ) do |format, mod, output, input|
80
+
81
+ describe "render_empty [#{format}]" do
82
+ let (:format) { format }
83
+
84
+ representer!(:module => mod) do
85
+ collection :songs, :render_empty => false
86
+ self.representation_wrap = :album if format == :xml
87
+ end
88
+
89
+ let (:album) { OpenStruct.new(:songs => []).extend(representer) }
90
+
91
+ it { render(album).must_equal_document output }
92
+ end
93
+ end
94
+ end
95
+
96
+
97
+ # wrap_test
98
+ for_formats(
99
+ :hash => [Representable::Hash, {}],
100
+ # :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>"],
101
+ # :yaml => [Representable::YAML, "---\nsong:\n name: Alive\n", "---\nsong:\n name: You've Taken Everything\n"],
102
+ ) do |format, mod, input|
103
+
104
+ describe "parsing [#{format}] with wrap where wrap is missing" do
105
+ representer!(:module => mod) do
106
+ self.representation_wrap = :song
107
+
108
+ property :title
109
+ end
110
+
111
+ it "doesn't change represented object" do
112
+ song.extend(representer).send("from_#{format}", input).title.must_equal "Resist Stance"
113
+ end
114
+ end
115
+ end
116
+ end