representable 2.4.1 → 3.0.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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -12
  3. data/CHANGES.md +6 -27
  4. data/README.md +28 -1326
  5. data/lib/representable.rb +4 -14
  6. data/lib/representable/binding.rb +3 -7
  7. data/lib/representable/definition.rb +1 -2
  8. data/lib/representable/populator.rb +35 -0
  9. data/lib/representable/version.rb +1 -1
  10. data/test/definition_test.rb +0 -7
  11. data/test/exec_context_test.rb +2 -2
  12. data/test/instance_test.rb +0 -19
  13. data/test/realistic_benchmark.rb +0 -13
  14. data/test/representable_test.rb +0 -16
  15. data/test/skip_test.rb +1 -1
  16. data/test/test_helper.rb +0 -2
  17. metadata +3 -65
  18. data/lib/representable/deprecations.rb +0 -127
  19. data/lib/representable/parse_strategies.rb +0 -93
  20. data/test-with-deprecations/as_test.rb +0 -65
  21. data/test-with-deprecations/benchmarking.rb +0 -83
  22. data/test-with-deprecations/binding_test.rb +0 -46
  23. data/test-with-deprecations/blaaaaaaaa_test.rb +0 -69
  24. data/test-with-deprecations/cached_test.rb +0 -147
  25. data/test-with-deprecations/class_test.rb +0 -119
  26. data/test-with-deprecations/coercion_test.rb +0 -52
  27. data/test-with-deprecations/config/inherit_test.rb +0 -135
  28. data/test-with-deprecations/config_test.rb +0 -122
  29. data/test-with-deprecations/decorator_scope_test.rb +0 -28
  30. data/test-with-deprecations/decorator_test.rb +0 -96
  31. data/test-with-deprecations/default_test.rb +0 -34
  32. data/test-with-deprecations/defaults_options_test.rb +0 -93
  33. data/test-with-deprecations/definition_test.rb +0 -264
  34. data/test-with-deprecations/example.rb +0 -310
  35. data/test-with-deprecations/examples/object.rb +0 -31
  36. data/test-with-deprecations/exec_context_test.rb +0 -93
  37. data/test-with-deprecations/features_test.rb +0 -70
  38. data/test-with-deprecations/filter_test.rb +0 -57
  39. data/test-with-deprecations/for_collection_test.rb +0 -74
  40. data/test-with-deprecations/generic_test.rb +0 -116
  41. data/test-with-deprecations/getter_setter_test.rb +0 -21
  42. data/test-with-deprecations/hash_bindings_test.rb +0 -87
  43. data/test-with-deprecations/hash_test.rb +0 -160
  44. data/test-with-deprecations/heritage_test.rb +0 -62
  45. data/test-with-deprecations/if_test.rb +0 -79
  46. data/test-with-deprecations/include_exclude_test.rb +0 -88
  47. data/test-with-deprecations/inherit_test.rb +0 -159
  48. data/test-with-deprecations/inline_test.rb +0 -272
  49. data/test-with-deprecations/instance_test.rb +0 -266
  50. data/test-with-deprecations/is_representable_test.rb +0 -77
  51. data/test-with-deprecations/json_test.rb +0 -355
  52. data/test-with-deprecations/lonely_test.rb +0 -239
  53. data/test-with-deprecations/mongoid_test.rb +0 -31
  54. data/test-with-deprecations/nested_test.rb +0 -115
  55. data/test-with-deprecations/object_test.rb +0 -60
  56. data/test-with-deprecations/parse_pipeline_test.rb +0 -64
  57. data/test-with-deprecations/parse_strategy_test.rb +0 -279
  58. data/test-with-deprecations/pass_options_test.rb +0 -27
  59. data/test-with-deprecations/pipeline_test.rb +0 -277
  60. data/test-with-deprecations/populator_test.rb +0 -105
  61. data/test-with-deprecations/prepare_test.rb +0 -67
  62. data/test-with-deprecations/private_options_test.rb +0 -18
  63. data/test-with-deprecations/reader_writer_test.rb +0 -19
  64. data/test-with-deprecations/realistic_benchmark.rb +0 -115
  65. data/test-with-deprecations/render_nil_test.rb +0 -21
  66. data/test-with-deprecations/represent_test.rb +0 -88
  67. data/test-with-deprecations/representable_test.rb +0 -511
  68. data/test-with-deprecations/schema_test.rb +0 -148
  69. data/test-with-deprecations/serialize_deserialize_test.rb +0 -33
  70. data/test-with-deprecations/skip_test.rb +0 -81
  71. data/test-with-deprecations/stringify_hash_test.rb +0 -41
  72. data/test-with-deprecations/test_helper.rb +0 -135
  73. data/test-with-deprecations/test_helper_test.rb +0 -25
  74. data/test-with-deprecations/uncategorized_test.rb +0 -67
  75. data/test-with-deprecations/user_options_test.rb +0 -15
  76. data/test-with-deprecations/wrap_test.rb +0 -152
  77. data/test-with-deprecations/xml_bindings_test.rb +0 -62
  78. data/test-with-deprecations/xml_test.rb +0 -503
  79. data/test-with-deprecations/yaml_test.rb +0 -162
  80. data/test/parse_strategy_test.rb +0 -279
@@ -1,93 +0,0 @@
1
- module Representable
2
- class Populator
3
- FindOrInstantiate = ->(input, options) {
4
- binding = options[:binding]
5
-
6
- object_class = binding[:class].(input, options)
7
- object = object_class.find_by(id: input["id"]) || object_class.new
8
- if options[:binding].array?
9
- # represented.songs[i] = model
10
- options[:represented].send(binding.getter)[options[:index]] = object
11
- else
12
- # represented.song = model
13
- options[:represented].send(binding.setter, object)
14
- end
15
-
16
- object
17
- }
18
-
19
- # pipeline: [StopOnExcluded, AssignName, ReadFragment, StopOnNotFound, OverwriteOnNil, AssignFragment, #<Representable::Function::CreateObject:0x9805a44>, #<Representable::Function::Decorate:0x9805a1c>, Deserialize, Set]
20
-
21
- def self.apply!(options)
22
- return unless populator = options[:populator]
23
-
24
- options[:parse_pipeline] = ->(input, options) do
25
- pipeline = Pipeline[*parse_functions] # TODO: AssignFragment
26
- pipeline = Pipeline::Insert.(pipeline, SetValue, delete: true) # remove the setter function.
27
- pipeline = Pipeline::Insert.(pipeline, populator, replace: CreateObject::Populator) # let the actual populator do the job.
28
- # puts pipeline.extend(Representable::Pipeline::Debug).inspect
29
- pipeline
30
- end
31
- end
32
- end
33
-
34
- FindOrInstantiate = Populator::FindOrInstantiate
35
-
36
- # Parse strategies are just a combination of representable's options. They save you from memoizing the
37
- # necessary parameters.
38
- #
39
- # Feel free to contribute your strategy if you think it's worth sharing!
40
- class ParseStrategy
41
- def self.apply!(options)
42
- return unless strategy = options[:parse_strategy]
43
-
44
- warn "[Representable] :parse_strategy is deprecated. Please use a populator."
45
-
46
- strategy = :proc if strategy.is_a?(::Proc)
47
-
48
- parse_strategies[strategy].apply!(name, options)
49
- end
50
-
51
- def self.parse_strategies
52
- {
53
- :sync => Sync,
54
- :find_or_instantiate => FindOrInstantiate,
55
- :proc => Proc
56
- }
57
- end
58
-
59
-
60
- # Using a lambda as parse_strategy does not set the parsed property for you.
61
- class Proc
62
- def self.apply!(name, options)
63
- options[:setter] = lambda { |*| }
64
- options[:pass_options] = true
65
- options[:instance] = options[:parse_strategy]
66
- end
67
- end
68
-
69
-
70
- class Sync
71
- def self.apply!(name, options)
72
- options[:setter] = lambda { |*args| }
73
- options[:pass_options] = true
74
- options[:instance] = options[:collection] ?
75
- lambda { |options| options[:binding].get(options)[options[:index]] } :
76
- lambda { |options| options[:binding].get(options) }
77
- end
78
- end
79
-
80
-
81
- # replaces current collection.
82
- class FindOrInstantiate
83
- def self.apply!(name, options)
84
- options[:pass_options] = true
85
- options[:instance] = lambda { |options|
86
- object_class = options[:binding][:class].evaluate(self, options)
87
-
88
- object_class.find_by({id: options[:fragment]["id"]}) or object_class.new
89
- }
90
- end
91
- end
92
- end
93
- end
@@ -1,65 +0,0 @@
1
- require 'test_helper'
2
-
3
- class AsTest < MiniTest::Spec
4
- for_formats(
5
- :hash => [Representable::Hash, {"title" => "Wie Es Geht"}, {"title" => "Revolution"}],
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("Revolution")) }
11
- let (:format) { format }
12
-
13
-
14
- describe "as: with :symbol" do
15
- representer!(:module => mod) do
16
- property :name, :as => :title
17
- end
18
-
19
- it { render(song).must_equal_document output }
20
- it { parse(song, input).name.must_equal "Wie Es Geht" }
21
- end
22
-
23
-
24
- describe "as: with lambda" do
25
- representer!(:module => mod) do
26
- property :name, :as => lambda { |*| "#{self.class}" }
27
- end
28
-
29
- it { render(song).must_equal_document({"Song" => "Revolution"}) }
30
- it { parse(song, {"Song" => "Wie Es Geht"}).name.must_equal "Wie Es Geht" }
31
- end
32
-
33
-
34
- describe "lambda arguments" do
35
- representer! do
36
- property :name, :as => lambda { |*args| args.inspect }
37
- end
38
-
39
- it { render(song, :volume => 1).must_equal_document({"[{:volume=>1}]" => "Revolution"}) }
40
- it { parse(song, {"[{:volume=>1}]" => "Wie Es Geht"}, :volume => 1).name.must_equal "Wie Es Geht" }
41
- end
42
- end
43
- end
44
-
45
-
46
- # hash: to_hash(wrap: ) is representation_wrap
47
-
48
- class AsXmlTest < MiniTest::Spec
49
- Band = Struct.new(:name, :label)
50
- Album = Struct.new(:band)
51
- Label = Struct.new(:name)
52
-
53
- representer!(module: Representable::XML, decorator: true) do
54
- self.representation_wrap = :album
55
- property :band, as: :combo do
56
- self.representation_wrap = :band
57
- property :name
58
- end
59
- end
60
-
61
- it do
62
- skip
63
- representer.new(Album.new(Band.new("Offspring"))).to_xml.must_equal ""
64
- end
65
- end
@@ -1,83 +0,0 @@
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
@@ -1,46 +0,0 @@
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) }
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)).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)).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) }
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).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")).default_for(nil).must_equal nil }
42
-
43
- # return nil if no :default
44
- it { Binding.new(Representable::Definition.new(:song)).default_for(nil).must_equal nil }
45
- end
46
- end
@@ -1,69 +0,0 @@
1
- require 'test_helper'
2
-
3
-
4
-
5
-
6
- # Include Inherit Module And Decorator Test
7
- class SchemaTest < MiniTest::Spec
8
- module Genre
9
- include Representable
10
- property :genre
11
- end
12
-
13
- module LinkFeature
14
- def self.included(base)
15
- base.extend(Link)
16
- end
17
-
18
- module Link
19
- def link
20
- end
21
- end
22
- end
23
-
24
-
25
- module Module
26
- include Representable::Hash
27
- feature LinkFeature
28
-
29
- property :title
30
- property :label do # extend: LabelModule
31
- property :name
32
- link # feature
33
-
34
- property :location do
35
- property :city
36
- link # feature.
37
- end
38
- end
39
-
40
- property :album, :extend => lambda { raise "don't manifest me!" } # this is not an inline decorator, don't manifest it.
41
-
42
-
43
- include Genre # Schema::Included::included is called!
44
- end
45
-
46
-
47
- class InheritDecorator < Representable::Decorator
48
- include Representable::Hash
49
-
50
- include Module
51
-
52
- property :label, inherit: true do # decorator.rb:27:in `initialize': superclass must be a Class (Module given)
53
- property :city
54
-
55
- property :location, :inherit => true do
56
- property :city
57
- end
58
- end
59
- end
60
-
61
-
62
- class InheritFromDecorator < InheritDecorator
63
- property :label, inherit: true do
64
- collection :employees do
65
- property :name
66
- end
67
- end
68
- end
69
- end
@@ -1,147 +0,0 @@
1
- require "test_helper"
2
- require 'ruby-prof'
3
-
4
- class CachedTest < MiniTest::Spec
5
- # TODO: also test with feature(Cached)
6
-
7
- module Model
8
- Song = Struct.new(:title, :composer)
9
- Album = Struct.new(:name, :songs, :artist)
10
- Artist = Struct.new(:name, :hidden_taste)
11
- end
12
-
13
- class SongRepresenter < Representable::Decorator
14
- include Representable::Hash
15
- feature Representable::Cached
16
-
17
- property :title, render_filter: lambda { |input, options| "#{input}:#{options[:options][:user_options]}" }
18
- property :composer, class: Model::Artist do
19
- property :name
20
- end
21
- end
22
-
23
- class AlbumRepresenter < Representable::Decorator
24
- include Representable::Hash
25
- include Representable::Cached
26
-
27
- property :name
28
- collection :songs, decorator: SongRepresenter, class: Model::Song
29
- end
30
-
31
-
32
- describe "serialization" do
33
- let (:album_hash) { {"name"=>"Louder And Even More Dangerous", "songs"=>[{"title"=>"Southbound:{:volume=>10}"}, {"title"=>"Jailbreak:{:volume=>10}"}]} }
34
-
35
- let (:song) { Model::Song.new("Jailbreak") }
36
- let (:song2) { Model::Song.new("Southbound") }
37
- let (:album) { Model::Album.new("Live And Dangerous", [song, song2, Model::Song.new("Emerald")]) }
38
- let (:representer) { AlbumRepresenter.new(album) }
39
-
40
- it do
41
- album2 = Model::Album.new("Louder And Even More Dangerous", [song2, song])
42
-
43
- # makes sure options are passed correctly.
44
- representer.to_hash(volume: 9).must_equal({"name"=>"Live And Dangerous",
45
- "songs"=>[{"title"=>"Jailbreak:{:volume=>9}"}, {"title"=>"Southbound:{:volume=>9}"}, {"title"=>"Emerald:{:volume=>9}"}]}) # called in Deserializer/Serializer
46
-
47
- # representer becomes reusable as it is stateless.
48
- # representer.update!(album2)
49
-
50
- # makes sure options are passed correctly.
51
- # representer.to_hash(volume:10).must_equal(album_hash)
52
- end
53
-
54
- # profiling
55
- it do
56
- representer.to_hash
57
-
58
- RubyProf.start
59
- representer.to_hash
60
- res = RubyProf.stop
61
-
62
- printer = RubyProf::FlatPrinter.new(res)
63
-
64
- data = StringIO.new
65
- printer.print(data)
66
- data = data.string
67
-
68
- printer.print(STDOUT)
69
-
70
- # 3 songs get decorated.
71
- data.must_match "3 Representable::Function::Decorate#call"
72
- # 3 nested decorator is instantiated for 3 Songs, though.
73
- data.must_match "3 <Class::Representable::Decorator>#prepare"
74
- # no Binding is instantiated at runtime.
75
- data.wont_match "Representable::Binding#initialize"
76
- # 2 mappers for Album, Song
77
- # data.must_match "2 Representable::Mapper::Methods#initialize"
78
- # title, songs, 3x title, composer
79
- data.must_match "8 Representable::Binding#render_pipeline"
80
- data.wont_match "render_functions"
81
- data.wont_match "Representable::Binding::Factories#render_functions"
82
- end
83
- end
84
-
85
-
86
- describe "deserialization" do
87
- let (:album_hash) {
88
- {
89
- "name"=>"Louder And Even More Dangerous",
90
- "songs"=>[
91
- {"title"=>"Southbound", "composer"=>{"name"=>"Lynott"}},
92
- {"title"=>"Jailbreak", "composer"=>{"name"=>"Phil Lynott"}},
93
- {"title"=>"Emerald"}
94
- ]
95
- }
96
- }
97
-
98
- it do
99
- album = Model::Album.new
100
-
101
- AlbumRepresenter.new(album).from_hash(album_hash)
102
-
103
- album.songs.size.must_equal 3
104
- album.name.must_equal "Louder And Even More Dangerous"
105
- album.songs[0].title.must_equal "Southbound"
106
- album.songs[0].composer.name.must_equal "Lynott"
107
- album.songs[1].title.must_equal "Jailbreak"
108
- album.songs[1].composer.name.must_equal "Phil Lynott"
109
- album.songs[2].title.must_equal "Emerald"
110
- album.songs[2].composer.must_equal nil
111
-
112
- # TODO: test options.
113
- end
114
-
115
- it "xxx" do
116
- representer = AlbumRepresenter.new(Model::Album.new)
117
- representer.from_hash(album_hash)
118
-
119
- RubyProf.start
120
- # puts "#{representer.class.representable_attrs.get(:songs).representer_module.representable_attrs.inspect}"
121
- representer.from_hash(album_hash)
122
- res = RubyProf.stop
123
-
124
- printer = RubyProf::FlatPrinter.new(res)
125
-
126
- data = StringIO.new
127
- printer.print(data)
128
- data = data.string
129
-
130
- # only 2 nested decorators are instantiated, Song, and Artist.
131
- data.must_match "5 <Class::Representable::Decorator>#prepare"
132
- # a total of 5 properties in the object graph.
133
- data.wont_match "Representable::Binding#initialize"
134
-
135
-
136
- data.wont_match "parse_functions" # no pipeline creation.
137
- data.must_match "10 Representable::Binding#parse_pipeline"
138
- # three mappers for Album, Song, composer
139
- # data.must_match "3 Representable::Mapper::Methods#initialize"
140
- # # 6 deserializers as the songs collection uses 2.
141
- # data.must_match "6 Representable::Deserializer#initialize"
142
- # # one populater for every property.
143
- # data.must_match "5 Representable::Populator#initialize"
144
- # printer.print(STDOUT)
145
- end
146
- end
147
- end