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