jat 0.0.1 → 0.0.3

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +21 -0
  4. data/jat.gemspec +37 -0
  5. data/lib/jat/attribute.rb +85 -0
  6. data/lib/jat/config.rb +38 -0
  7. data/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads.rb +29 -0
  8. data/lib/jat/plugins/_activerecord_preloads/lib/preloader.rb +89 -0
  9. data/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord.rb +22 -0
  10. data/lib/jat/plugins/_json_api_activerecord/lib/preloads.rb +84 -0
  11. data/lib/jat/plugins/_preloads/_preloads.rb +53 -0
  12. data/lib/jat/plugins/_preloads/lib/format_user_preloads.rb +52 -0
  13. data/lib/jat/plugins/_preloads/lib/preloads_with_path.rb +78 -0
  14. data/lib/jat/plugins/cache/cache.rb +39 -0
  15. data/lib/jat/plugins/camel_lower/camel_lower.rb +18 -0
  16. data/lib/jat/plugins/json_api/json_api.rb +207 -0
  17. data/lib/jat/plugins/json_api/lib/construct_traversal_map.rb +91 -0
  18. data/lib/jat/plugins/json_api/lib/map.rb +54 -0
  19. data/lib/jat/plugins/json_api/lib/params/fields/parse.rb +27 -0
  20. data/lib/jat/plugins/json_api/lib/params/fields/validate.rb +55 -0
  21. data/lib/jat/plugins/json_api/lib/params/fields.rb +23 -0
  22. data/lib/jat/plugins/json_api/lib/params/include/parse.rb +55 -0
  23. data/lib/jat/plugins/json_api/lib/params/include/validate.rb +29 -0
  24. data/lib/jat/plugins/json_api/lib/params/include.rb +49 -0
  25. data/lib/jat/plugins/json_api/lib/presenters/document_links_presenter.rb +48 -0
  26. data/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter.rb +48 -0
  27. data/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter.rb +48 -0
  28. data/lib/jat/plugins/json_api/lib/presenters/links_presenter.rb +48 -0
  29. data/lib/jat/plugins/json_api/lib/presenters/meta_presenter.rb +48 -0
  30. data/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter.rb +53 -0
  31. data/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter.rb +53 -0
  32. data/lib/jat/plugins/json_api/lib/response.rb +239 -0
  33. data/lib/jat/plugins/json_api/lib/traversal_map.rb +34 -0
  34. data/lib/jat/plugins/simple_api/lib/construct_traversal_map.rb +45 -0
  35. data/lib/jat/plugins/simple_api/lib/map.rb +29 -0
  36. data/lib/jat/plugins/simple_api/lib/params/parse.rb +68 -0
  37. data/lib/jat/plugins/simple_api/lib/response.rb +134 -0
  38. data/lib/jat/plugins/simple_api/simple_api.rb +65 -0
  39. data/lib/jat/plugins/to_str/to_str.rb +44 -0
  40. data/lib/jat/plugins.rb +39 -0
  41. data/lib/jat/presenter.rb +51 -0
  42. data/lib/jat/utils/enum_deep_dup.rb +29 -0
  43. data/lib/jat/utils/enum_deep_freeze.rb +19 -0
  44. data/lib/jat.rb +66 -144
  45. data/test/lib/jat/attribute_test.rb +142 -0
  46. data/test/lib/jat/config_test.rb +57 -0
  47. data/test/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads_test.rb +40 -0
  48. data/test/lib/jat/plugins/_activerecord_preloads/lib/preloader_test.rb +98 -0
  49. data/test/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord_test.rb +29 -0
  50. data/test/lib/jat/plugins/_json_api_activerecord/lib/preloads_test.rb +191 -0
  51. data/test/lib/jat/plugins/_preloads/_preloads_test.rb +68 -0
  52. data/test/lib/jat/plugins/_preloads/lib/format_user_preloads_test.rb +47 -0
  53. data/test/lib/jat/plugins/_preloads/lib/preloads_with_path_test.rb +33 -0
  54. data/test/lib/jat/plugins/cache/cache_test.rb +82 -0
  55. data/test/lib/jat/plugins/camel_lower/camel_lower_test.rb +78 -0
  56. data/test/lib/jat/plugins/json_api/json_api_test.rb +154 -0
  57. data/test/lib/jat/plugins/json_api/lib/construct_traversal_map_test.rb +119 -0
  58. data/test/lib/jat/plugins/json_api/lib/map_test.rb +117 -0
  59. data/test/lib/jat/plugins/json_api/lib/params/fields/parse_test.rb +24 -0
  60. data/test/lib/jat/plugins/json_api/lib/params/fields/validate_test.rb +47 -0
  61. data/test/lib/jat/plugins/json_api/lib/params/fields_test.rb +37 -0
  62. data/test/lib/jat/plugins/json_api/lib/params/include/parse_test.rb +46 -0
  63. data/test/lib/jat/plugins/json_api/lib/params/include/validate_test.rb +51 -0
  64. data/test/lib/jat/plugins/json_api/lib/params/include_test.rb +41 -0
  65. data/test/lib/jat/plugins/json_api/lib/presenters/document_links_presenter_test.rb +69 -0
  66. data/test/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter_test.rb +69 -0
  67. data/test/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter_test.rb +69 -0
  68. data/test/lib/jat/plugins/json_api/lib/presenters/links_presenter_test.rb +69 -0
  69. data/test/lib/jat/plugins/json_api/lib/presenters/meta_presenter_test.rb +69 -0
  70. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter_test.rb +75 -0
  71. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter_test.rb +75 -0
  72. data/test/lib/jat/plugins/json_api/lib/response_test.rb +489 -0
  73. data/test/lib/jat/plugins/json_api/lib/traversal_map_test.rb +58 -0
  74. data/test/lib/jat/plugins/simple_api/lib/construct_traversal_map_test.rb +100 -0
  75. data/test/lib/jat/plugins/simple_api/lib/map_test.rb +56 -0
  76. data/test/lib/jat/plugins/simple_api/lib/params/parse_test.rb +71 -0
  77. data/test/lib/jat/plugins/simple_api/lib/response_test.rb +342 -0
  78. data/test/lib/jat/plugins/simple_api/simple_api_test.rb +81 -0
  79. data/test/lib/jat/plugins/to_str/to_str_test.rb +52 -0
  80. data/test/lib/jat/presenter_test.rb +61 -0
  81. data/test/lib/jat/utils/enum_deep_dup_test.rb +31 -0
  82. data/test/lib/jat/utils/enum_deep_freeze_test.rb +28 -0
  83. data/test/lib/jat_test.rb +120 -0
  84. data/test/lib/plugin_test.rb +49 -0
  85. data/test/support/activerecord.rb +24 -0
  86. data/test/test_helper.rb +16 -0
  87. data/test/test_plugin.rb +59 -0
  88. metadata +240 -11
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe Jat::Attribute do
6
+ let(:jat_class) { Class.new(Jat) }
7
+ let(:attribute_class) { jat_class::Attribute }
8
+
9
+ describe ".jat_class=" do
10
+ it "assigns @jat_class" do
11
+ attribute_class.jat_class = :foo
12
+ assert_equal :foo, attribute_class.instance_variable_get(:@jat_class)
13
+ end
14
+ end
15
+
16
+ describe ".jat_class" do
17
+ it "returns self @jat_class" do
18
+ assert_same jat_class, attribute_class.instance_variable_get(:@jat_class)
19
+ assert_same jat_class, attribute_class.jat_class
20
+ end
21
+ end
22
+
23
+ describe ".inspect" do
24
+ it "returns self name" do
25
+ assert_equal "#{jat_class}::Attribute", attribute_class.inspect
26
+ end
27
+ end
28
+
29
+ describe "#initialize" do
30
+ it "deeply copies provided params" do
31
+ params = {name: :foo, opts: {include: {foo: :bar}}, block: -> {}}
32
+
33
+ attribute = attribute_class.new(**params)
34
+ assert_equal attribute.params, params
35
+ refute_same attribute.params, params
36
+ refute_same attribute.opts, params[:opts]
37
+ end
38
+ end
39
+
40
+ describe "#name" do
41
+ it "symbolizes name" do
42
+ attribute = attribute_class.new(name: "foo")
43
+ assert_equal :foo, attribute.name
44
+ end
45
+ end
46
+
47
+ describe "#key" do
48
+ it "returns same as #name when key not provided" do
49
+ attribute = attribute_class.new(name: "foo")
50
+ assert_equal :foo, attribute.key
51
+ end
52
+
53
+ it "returns symbolized key option" do
54
+ attribute = attribute_class.new(name: "foo", opts: {key: "bar"})
55
+ assert_equal :bar, attribute.key
56
+ end
57
+ end
58
+
59
+ describe "#exposed?" do
60
+ it "returns provided value" do
61
+ attribute = attribute_class.new(name: "foo", opts: {exposed: false})
62
+ assert_equal false, attribute.exposed?
63
+ end
64
+
65
+ it "returns true when all keys are exposed by config" do
66
+ jat_class.config[:exposed] = :all
67
+ attribute = attribute_class.new(name: "foo")
68
+ assert_equal true, attribute.exposed?
69
+ end
70
+
71
+ it "returns false when no keys are exposed by config" do
72
+ jat_class.config[:exposed] = :none
73
+ attribute = attribute_class.new(name: "foo")
74
+ assert_equal false, attribute.exposed?
75
+ end
76
+
77
+ it "returns false when serializer exists and config[:exposed] is default" do
78
+ jat_class.config[:exposed] = :default
79
+ attribute = attribute_class.new(name: "foo", opts: {serializer: jat_class})
80
+ assert_equal false, attribute.exposed?
81
+ end
82
+
83
+ it "returns true when serializer not exists and config[:exposed] is default" do
84
+ jat_class.config[:exposed] = :default
85
+ attribute = attribute_class.new(name: "foo")
86
+ assert_equal true, attribute.exposed?
87
+ end
88
+ end
89
+
90
+ describe "#many?" do
91
+ it "returns nil when no key provided" do
92
+ attribute = attribute_class.new(name: "foo")
93
+ assert_nil attribute.many?
94
+ end
95
+
96
+ it "returns provided data" do
97
+ attribute = attribute_class.new(name: "foo", opts: {many: false})
98
+ assert_equal false, attribute.many?
99
+ assert_equal false, attribute.instance_variable_get(:@many)
100
+ assert_same attribute.instance_variable_get(:@many), attribute.many?
101
+ end
102
+ end
103
+
104
+ describe "#relation?" do
105
+ it "returns false when no serializer key" do
106
+ attribute = attribute_class.new(name: "foo")
107
+ assert_equal false, attribute.relation?
108
+ end
109
+
110
+ it "returns true with serializer key" do
111
+ attribute = attribute_class.new(name: "foo", opts: {serializer: jat_class})
112
+ assert_equal true, attribute.relation?
113
+ end
114
+ end
115
+
116
+ describe "#serializer" do
117
+ it "returns nil when no serializer key" do
118
+ attribute = attribute_class.new(name: "foo")
119
+ assert_nil attribute.serializer
120
+ end
121
+
122
+ it "returns provided serializer" do
123
+ attribute = attribute_class.new(name: "foo", opts: {serializer: jat_class})
124
+ assert_same jat_class, attribute.serializer
125
+ end
126
+ end
127
+
128
+ describe "#block" do
129
+ it "returns provided block" do
130
+ block = -> {}
131
+ attribute = attribute_class.new(name: "foo", block: block)
132
+ assert_equal block, attribute.block
133
+ end
134
+
135
+ it "constructs block that calls current key method on object" do
136
+ attribute = attribute_class.new(name: "foo", opts: {key: :length})
137
+ presenter = jat_class::Presenter.new([1, 2, 3], {})
138
+
139
+ assert_equal 3, presenter.instance_exec(&attribute.block)
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe Jat::Config do
6
+ let(:jat_class) { Class.new(Jat) }
7
+ let(:config) { jat_class.config }
8
+
9
+ describe ".jat_class=" do
10
+ it "assigns @jat_class" do
11
+ config.class.jat_class = :foo
12
+ assert_equal :foo, config.class.instance_variable_get(:@jat_class)
13
+ end
14
+ end
15
+
16
+ describe ".jat_class" do
17
+ it "returns self @jat_class" do
18
+ assert_same jat_class, config.class.instance_variable_get(:@jat_class)
19
+ assert_same jat_class, config.class.jat_class
20
+ end
21
+ end
22
+
23
+ describe ".inspect" do
24
+ it "returns self name" do
25
+ assert_equal "#{jat_class}::Config", config.class.inspect
26
+ end
27
+ end
28
+
29
+ describe "#initialize" do
30
+ it "deeply copies provided opts" do
31
+ opts = {foo: {bar: {bazz: :bazz2}}}
32
+
33
+ config = Jat::Config.new(opts)
34
+ assert_equal config.opts, opts
35
+ refute_same config.opts[:foo], opts[:foo]
36
+ refute_same config.opts[:foo][:bar], opts[:foo][:bar]
37
+ end
38
+ end
39
+
40
+ describe "#[]=" do
41
+ it "adds options" do
42
+ config = Jat::Config.new
43
+ config[:foo] = :bar
44
+
45
+ assert_equal :bar, config.opts[:foo]
46
+ end
47
+ end
48
+
49
+ describe "#[]" do
50
+ it "reads options" do
51
+ config = Jat::Config.new
52
+ config[:foo] = :bar
53
+
54
+ assert_equal :bar, config[:foo]
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::ActiverecordPreloads" do
6
+ let(:jat_class) { Class.new(Jat) { plugin(:_activerecord_preloads) } }
7
+ let(:preloader) { Jat::Plugins::ActiverecordPreloads::Preloader }
8
+
9
+ describe "InstanceMethods" do
10
+ it "adds preloads to object in initialize call" do
11
+ obj = "OBJ"
12
+ preloads = "PRELOADS"
13
+ jat_class.expects(:jat_preloads).with(kind_of(jat_class)).returns(preloads)
14
+ preloader.expects(:preload).with(obj, preloads).returns("OBJ_WITH_PRELOADS")
15
+
16
+ jat = jat_class.new(obj, {})
17
+ assert_equal("OBJ_WITH_PRELOADS", jat.object)
18
+ end
19
+
20
+ it "skips preloadings for nil" do
21
+ object = nil
22
+ jat = jat_class.new(object, {})
23
+ assert_same object, jat.object
24
+ end
25
+
26
+ it "skips preloadings for empty array" do
27
+ object = []
28
+ jat = jat_class.new(object, {})
29
+ assert_same object, jat.object
30
+ end
31
+
32
+ it "skips preloadings when nothing to preload" do
33
+ object = "OBJECT"
34
+ jat_class.expects(:jat_preloads).with(kind_of(jat_class)).returns({})
35
+
36
+ jat = jat_class.new(object, {})
37
+ assert_same object, jat.object
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+ require "support/activerecord"
5
+
6
+ describe "Jat::Plugins::ActiverecordPreloads" do
7
+ before { Jat::Plugins.load_plugin(:_activerecord_preloads) }
8
+
9
+ describe "Preloader" do
10
+ let(:described_class) { plugin::Preloader }
11
+ let(:plugin) { Jat::Plugins::ActiverecordPreloads }
12
+
13
+ describe ".handlers" do
14
+ it "returns memorized array of handlers" do
15
+ assert_equal [
16
+ plugin::ActiverecordRelation,
17
+ plugin::ActiverecordObject,
18
+ plugin::ActiverecordArray
19
+ ], described_class.handlers
20
+
21
+ assert_same described_class.handlers, described_class.handlers
22
+ end
23
+ end
24
+
25
+ describe ".preload" do
26
+ it "raises error when can't find appropriate handler" do
27
+ preloads = {}
28
+
29
+ object = nil
30
+ err = assert_raises(Jat::Error) { described_class.preload(object, {}) }
31
+ assert_equal "Can't preload #{preloads.inspect} to #{object.inspect}", err.message
32
+
33
+ object = []
34
+ err = assert_raises(Jat::Error) { described_class.preload(object, {}) }
35
+ assert_equal "Can't preload #{preloads.inspect} to #{object.inspect}", err.message
36
+
37
+ object = 123
38
+ err = assert_raises(Jat::Error) { described_class.preload(object, {}) }
39
+ assert_equal "Can't preload #{preloads.inspect} to #{object.inspect}", err.message
40
+
41
+ object = [AR::User.create!, AR::Comment.create!]
42
+ err = assert_raises(Jat::Error) { described_class.preload(object, {}) }
43
+ assert_equal "Can't preload #{preloads.inspect} to #{object.inspect}", err.message
44
+ end
45
+
46
+ it "preloads data to activerecord object" do
47
+ user = AR::User.create!
48
+
49
+ result = described_class.preload(user, {comments: {}})
50
+
51
+ assert_same result, user
52
+ assert_equal true, user.association(:comments).loaded?
53
+ end
54
+
55
+ it "resets activerecord object preloaded relations" do
56
+ user = AR::User.create!
57
+ comment = user.comments.create!
58
+ user.comments.to_a && comment.delete # preload comments manually and delete comment
59
+
60
+ described_class.preload(user, {comments: {}})
61
+
62
+ assert_equal true, user.association(:comments).loaded?
63
+ assert_equal [], user.comments
64
+ end
65
+
66
+ it "preloads data to activerecord relation object" do
67
+ user = AR::User.create!
68
+
69
+ result = described_class.preload(AR::User.where(id: user.id), {comments: {}})
70
+
71
+ assert_equal result, [user]
72
+ assert_equal true, result[0].association(:comments).loaded?
73
+ end
74
+
75
+ it "preloads data to activerecord array" do
76
+ user = AR::User.create!
77
+
78
+ users = [user]
79
+ result = described_class.preload(users, {comments: {}})
80
+
81
+ assert_same result, users
82
+ assert_equal true, result[0].association(:comments).loaded?
83
+ end
84
+
85
+ it "resets activerecord array preloaded relations" do
86
+ user = AR::User.create!
87
+ comment = user.comments.create!
88
+ user.comments.to_a && comment.delete # preload comments manually and delete comment
89
+
90
+ users = [user]
91
+ result = described_class.preload(users, {comments: {}})
92
+
93
+ assert_equal true, result[0].association(:comments).loaded?
94
+ assert_equal [], result[0].comments
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::JsonApiActiverecord" do
6
+ before do
7
+ @plugin = Jat::Plugins.load_plugin(:_json_api_activerecord)
8
+ end
9
+
10
+ it "loads other plugins in after_load" do
11
+ jat_class = Class.new(Jat)
12
+ jat_class.expects(:plugin).with(:_preloads, foo: :bar)
13
+ jat_class.expects(:plugin).with(:_activerecord_preloads, foo: :bar)
14
+
15
+ Jat::Plugins.after_load(@plugin, jat_class, foo: :bar)
16
+ end
17
+
18
+ describe "ClassMethods" do
19
+ it "add .jat_preloads method as a delegator to #{@plugin}::Preloads" do
20
+ jat_class = Class.new(Jat)
21
+ jat_class.plugin @plugin
22
+ jat = jat_class.allocate
23
+
24
+ @plugin::Preloads.expects(:call).with(jat).returns("RES")
25
+
26
+ assert_equal "RES", jat_class.jat_preloads(jat)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::JsonApiActiverecord::Preloads" do
6
+ let(:base) { Class.new(Jat) { plugin :json_api, activerecord: true } }
7
+
8
+ let(:user_serializer) { Class.new(base) { type(:user) } }
9
+ let(:profile_serializer) { Class.new(base) { type(:profile) } }
10
+ let(:email_serializer) { Class.new(base) { type(:email) } }
11
+
12
+ let(:jat_user) { user_serializer.allocate }
13
+ let(:jat_profile) { profile_serializer.allocate }
14
+ let(:jat_email) { email_serializer.allocate }
15
+
16
+ let(:described_class) { Jat::Plugins::JsonApiActiverecord::Preloads }
17
+
18
+ def define_map(map)
19
+ jat_user.traversal_map.expects(:current).returns(map)
20
+ end
21
+
22
+ it "returns empty hash when no attributes requested" do
23
+ define_map(user: {attributes: [], relationships: []})
24
+
25
+ result = described_class.call(jat_user)
26
+ assert_equal({}, result)
27
+ end
28
+
29
+ it "returns empty hash when no attributes with preloads requested" do
30
+ user_serializer.attribute :name
31
+ define_map(user: {attributes: [:name], relationships: []})
32
+
33
+ result = described_class.call(jat_user)
34
+ assert_equal({}, result)
35
+ end
36
+
37
+ it "returns preloads for requested attributes" do
38
+ user_serializer.attribute :name, preload: :profile
39
+ define_map(user: {attributes: [:name], relationships: []})
40
+
41
+ result = described_class.call(jat_user)
42
+ assert_equal({profile: {}}, result)
43
+ end
44
+
45
+ it "returns merged preloads for requested attributes" do
46
+ user_serializer.attribute :first_name, preload: :profile
47
+ user_serializer.attribute :phone, preload: {profile: :phones}
48
+ user_serializer.attribute :email, preload: {profile: :emails}
49
+
50
+ define_map(user: {attributes: %i[first_name phone email], relationships: []})
51
+
52
+ result = described_class.call(jat_user)
53
+ assert_equal({profile: {phones: {}, emails: {}}}, result)
54
+ end
55
+
56
+ it "returns no preloads and no nested preloads for relationships when specified preloads is nil" do
57
+ user_serializer.relationship :profile, serializer: profile_serializer, preload: nil
58
+ profile_serializer.attribute :email, preload: :email # should not be preloaded
59
+ define_map(
60
+ user: {attributes: [], relationships: %i[profile]},
61
+ profile: {attributes: %i[email], relationships: []}
62
+ )
63
+
64
+ result = described_class.call(jat_user)
65
+ assert_equal({}, result)
66
+ end
67
+
68
+ it "returns preloads for nested relationships joined to root when specified preloads is {} or []" do
69
+ [{}, []].each do |preloads|
70
+ user_serializer.relationship :profile, serializer: profile_serializer, preload: preloads
71
+ profile_serializer.attribute :email, preload: :email # should be preloaded to root
72
+ define_map(
73
+ user: {attributes: [], relationships: %i[profile]},
74
+ profile: {attributes: %i[email], relationships: []}
75
+ )
76
+
77
+ result = described_class.call(jat_user)
78
+ assert_equal({email: {}}, result)
79
+ end
80
+ end
81
+
82
+ it "returns preloads for relationships" do
83
+ user_serializer.relationship :profile, serializer: profile_serializer
84
+ define_map(
85
+ user: {attributes: [], relationships: %i[profile]},
86
+ profile: {attributes: [], relationships: []}
87
+ )
88
+
89
+ result = described_class.call(jat_user)
90
+ assert_equal({profile: {}}, result)
91
+ end
92
+
93
+ it "returns nested preloads for relationships" do
94
+ user_serializer.relationship :profile, serializer: profile_serializer
95
+ profile_serializer.attribute :email, preload: %i[confirmed_email unconfirmed_email]
96
+
97
+ define_map(
98
+ user: {attributes: [], relationships: %i[profile]},
99
+ profile: {attributes: %i[email], relationships: []}
100
+ )
101
+
102
+ result = described_class.call(jat_user)
103
+ assert_equal({profile: {confirmed_email: {}, unconfirmed_email: {}}}, result)
104
+ end
105
+
106
+ it "preloads nested relationships for nested relationship" do
107
+ user_serializer.relationship :profile, serializer: profile_serializer, preload: {company: :profile}
108
+ profile_serializer.attribute :email, preload: %i[confirmed_email unconfirmed_email]
109
+
110
+ define_map(
111
+ user: {attributes: [], relationships: %i[profile]},
112
+ profile: {attributes: %i[email], relationships: []}
113
+ )
114
+
115
+ result = described_class.call(jat_user)
116
+ assert_equal({company: {profile: {confirmed_email: {}, unconfirmed_email: {}}}}, result)
117
+ end
118
+
119
+ it "preloads nested relationships to main (!) resource" do
120
+ user_serializer.relationship :profile, serializer: profile_serializer, preload: {company!: :profile}
121
+ profile_serializer.attribute :email, preload: %i[confirmed_email unconfirmed_email]
122
+
123
+ define_map(
124
+ user: {attributes: [], relationships: %i[profile]},
125
+ profile: {attributes: %i[email], relationships: []}
126
+ )
127
+
128
+ result = described_class.call(jat_user)
129
+ assert_equal({company: {profile: {}, confirmed_email: {}, unconfirmed_email: {}}}, result)
130
+ end
131
+
132
+ it "raises error if with 2 serializers have recursive preloads" do
133
+ user_serializer.relationship :profile, serializer: profile_serializer, preload: :profile
134
+ profile_serializer.relationship :user, serializer: user_serializer, preload: :user
135
+
136
+ define_map(
137
+ user: {attributes: [], relationships: %i[profile]},
138
+ profile: {attributes: [], relationships: %i[user]}
139
+ )
140
+
141
+ error = assert_raises(Jat::Error) { described_class.call(jat_user) }
142
+ assert_match(/recursive preloads/, error.message)
143
+ end
144
+
145
+ it "raises error if 3 serializers recursive preloads" do
146
+ user_serializer.relationship :profile, serializer: profile_serializer, preload: :profile
147
+ profile_serializer.relationship :email, serializer: email_serializer, preload: :email
148
+ email_serializer.relationship :user, serializer: user_serializer, preload: :user
149
+
150
+ define_map(
151
+ user: {attributes: [], relationships: %i[profile]},
152
+ profile: {attributes: [], relationships: %i[email]},
153
+ email: {attributes: [], relationships: %i[user]}
154
+ )
155
+
156
+ error = assert_raises(Jat::Error) { described_class.call(jat_user) }
157
+ assert_match(/recursive preloads/, error.message)
158
+ end
159
+
160
+ it "does not raises error if 2 serializers preloads same preloads" do
161
+ user_serializer.relationship :profile, serializer: profile_serializer, preload: :profile
162
+ user_serializer.relationship :email, serializer: email_serializer, preload: :email
163
+ profile_serializer.relationship :email, serializer: email_serializer, preload: :email
164
+
165
+ define_map(
166
+ user: {attributes: [], relationships: %i[profile email]},
167
+ profile: {attributes: [], relationships: %i[email]},
168
+ email: {attributes: [], relationships: %i[]}
169
+ )
170
+
171
+ described_class.call(jat_user) # should not raise
172
+ end
173
+
174
+ it "merges preloads the same way regardless of order of preloads" do
175
+ a = Class.new(base) { type(:a) }
176
+ a.attribute :a1, preload: {foo: {bar: {bazz1: {}, bazz: {}}}}
177
+ a.attribute :a2, preload: {foo: {bar: {bazz2: {}, bazz: {last: {}}}}}
178
+
179
+ jat_a1 = a.allocate
180
+ jat_a2 = a.allocate
181
+
182
+ jat_a1.traversal_map.expects(:current).returns(a: {attributes: %i[a1 a2], relationships: %i[]})
183
+ jat_a2.traversal_map.expects(:current).returns(a: {attributes: %i[a2 a1], relationships: %i[]})
184
+
185
+ result1 = described_class.call(jat_a1)
186
+ result2 = described_class.call(jat_a2)
187
+
188
+ assert_equal(result2, result1)
189
+ assert_equal({foo: {bar: {bazz: {last: {}}, bazz1: {}, bazz2: {}}}}, result1)
190
+ end
191
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::Preloads" do
6
+ let(:jat_class) do
7
+ Class.new(Jat) { plugin(:_preloads) }
8
+ end
9
+
10
+ def attribute(name, opts)
11
+ jat_class.attribute(name, **opts)
12
+ end
13
+
14
+ describe "AttributeMethods" do
15
+ it "saves @preloads and @preloads_path after first access" do
16
+ attribute = attribute(:foo, preload: :bar)
17
+
18
+ assert_same attribute.preloads, attribute.preloads
19
+ assert_same attribute.preloads_path, attribute.preloads_path
20
+ end
21
+
22
+ describe "#preloads" do
23
+ it "returns formatted data provided by user" do
24
+ assert_equal({}, attribute(:foo, preload: {}).preloads)
25
+ assert_equal({}, attribute(:foo, preload: []).preloads)
26
+ assert_equal({some: {}}, attribute(:foo, preload: :some).preloads)
27
+ assert_equal({some: {data: {}}}, attribute(:foo, preload: {some: :data}).preloads)
28
+ end
29
+
30
+ it "preloads main key when serializer exists and preloads are not" do
31
+ assert_equal({foo: {}}, attribute(:foo, serializer: jat_class).preloads)
32
+ assert_equal({bar: {}}, attribute(:foo, key: :bar, serializer: jat_class).preloads)
33
+ end
34
+
35
+ it "returns nil when requested to preload nil or false" do
36
+ assert_nil attribute(:foo, serializer: jat_class, preload: nil).preloads
37
+ assert_nil attribute(:foo, key: :bar, serializer: jat_class, preload: false).preloads
38
+ end
39
+
40
+ it "removes bang (!) from preloads" do
41
+ assert_equal({foo: {}}, attribute(:foo, preload: :foo!).preloads)
42
+ end
43
+ end
44
+
45
+ describe "#preloads_path" do
46
+ it "returns path to main resource provided by user" do
47
+ assert_equal([], attribute(:foo, preload: nil).preloads_path)
48
+ assert_equal([], attribute(:foo, preload: {}).preloads_path)
49
+ assert_equal([], attribute(:foo, preload: []).preloads_path)
50
+ assert_equal([:some], attribute(:foo, preload: :some).preloads_path)
51
+ assert_equal(%i[some data], attribute(:foo, preload: {some: :data}).preloads_path)
52
+ end
53
+
54
+ it "show path to main key when serializer exists and preloads are not" do
55
+ assert_equal([:foo], attribute(:foo, serializer: jat_class).preloads_path)
56
+ assert_equal([:bar], attribute(:foo, key: :bar, serializer: jat_class).preloads_path)
57
+
58
+ # serializer with empty preloads provided
59
+ assert_equal([], attribute(:foo, serializer: jat_class, preload: nil).preloads_path)
60
+ assert_equal([], attribute(:foo, key: :bar, serializer: jat_class, preload: false).preloads_path)
61
+ end
62
+
63
+ it "removes bang (!) and construct path to this preload" do
64
+ assert_equal([:some], attribute(:foo, preload: {some!: :data}).preloads_path)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ describe "Jat::Plugins::Preloads::FormatUserPreloads" do
6
+ before { Jat::Plugins.load_plugin(:_preloads) }
7
+
8
+ let(:format) { Jat::Plugins::Preloads::FormatUserPreloads }
9
+
10
+ it "transforms nil to empty hash" do
11
+ preloads = nil
12
+ assert_equal ({}), format.to_hash(preloads)
13
+ end
14
+
15
+ it "transforms false to empty hash" do
16
+ preloads = false
17
+ assert_equal({}, format.to_hash(preloads))
18
+ end
19
+
20
+ it "transforms Symbol" do
21
+ preloads = :foo
22
+ assert_equal({foo: {}}, format.to_hash(preloads))
23
+ end
24
+
25
+ it "transforms String" do
26
+ preloads = "foo"
27
+ assert_equal({foo: {}}, format.to_hash(preloads))
28
+ end
29
+
30
+ it "transforms Hash" do
31
+ preloads = {foo: :bar}
32
+ assert_equal({foo: {bar: {}}}, format.to_hash(preloads))
33
+ end
34
+
35
+ it "transforms Array" do
36
+ preloads = %i[foo bar]
37
+ assert_equal({foo: {}, bar: {}}, format.to_hash(preloads))
38
+ end
39
+
40
+ it "transforms nested hashes and arrays" do
41
+ preloads = [:foo, {"bar" => "bazz"}, ["bazz"]]
42
+ assert_equal({foo: {}, bar: {bazz: {}}, bazz: {}}, format.to_hash(preloads))
43
+
44
+ preloads = {"bar" => "bazz", :foo => [:bar, "bazz"]}
45
+ assert_equal({bar: {bazz: {}}, foo: {bar: {}, bazz: {}}}, format.to_hash(preloads))
46
+ end
47
+ end