jsonapi-materializer 1.0.0.rc.pre.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,102 +1,103 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require("spec_helper")
2
4
 
3
5
  RSpec.describe(JSONAPI::Materializer::Collection) do
4
- let(:described_class) {ArticleMaterializer::Collection}
5
- let(:policy) {Class.new { def read_attribute?(name); true end}}
6
- let(:collection) {described_class.new(:object => object, :includes => [["comments"], ["author"]], :context => {:policy => policy.new})}
6
+ let(:described_class) { ArticleMaterializer::Collection }
7
+ let(:collection) { described_class.new(object:, includes: [["comments"], ["author"]]) }
7
8
 
8
9
  describe("#as_json") do
9
- subject {collection.as_json.deep_stringify_keys}
10
+ subject { collection.as_json.deep_stringify_keys }
10
11
 
11
12
  before do
12
- Account.create!(:id => 9, :name => "Dan Gebhardt", :twitter => "dgeb")
13
- Account.create!(:id => 2, :name => "DHH", :twitter => "DHH")
14
- Article.create!(:id => 1, :title => "JSON API paints my bikeshed!", :account => Account.find(9))
15
- Article.create!(:id => 2, :title => "Rails is Omakase", :account => Account.find(9))
16
- Article.create!(:id => 3, :title => "What is JSON:API?", :account => Account.find(9))
17
- Comment.create!(:id => 5, :body => "First!", :article => Article.find(1), :account => Account.find(2))
18
- Comment.create!(:id => 12, :body => "I like XML better", :article => Article.find(1), :account => Account.find(9))
13
+ Account.create!(id: 9, name: "Dan Gebhardt", twitter: "dgeb")
14
+ Account.create!(id: 2, name: "DHH", twitter: "DHH")
15
+ Article.create!(id: 1, title: "JSON API paints my bikeshed!", account: Account.find(9))
16
+ Article.create!(id: 2, title: "Rails is Omakase", account: Account.find(9))
17
+ Article.create!(id: 3, title: "What is JSON:API?", account: Account.find(9))
18
+ Comment.create!(id: 5, body: "First!", article: Article.find(1), account: Account.find(2))
19
+ Comment.create!(id: 12, body: "I like XML better", article: Article.find(1), account: Account.find(9))
19
20
  end
20
21
 
21
22
  context("when the list has items") do
22
- let(:object) {Kaminari.paginate_array(Article.all).page(1).per(1)}
23
+ let(:object) { Kaminari.paginate_array(Article.all).page(1).per(1) }
23
24
 
24
25
  it("has a data key at root with the resources") do
25
26
  expect(subject.fetch("data")).to(eq([{
26
- "id" => "1",
27
- "type" => "articles",
28
- "attributes" => {
29
- "title" => "JSON API paints my bikeshed!"
30
- },
31
- "relationships" => {
32
- "author" => {
33
- "data" => {"id" => "9", "type" => "people"},
34
- "links" => {
35
- "self" => "http://example.com/articles/1/relationships/author",
36
- "related" => "http://example.com/articles/1/author"
37
- }
38
- },
39
- "comments" => {
40
- "data" => [
41
- {"id" => "5", "type" => "comments"},
42
- {"id" => "12", "type" => "comments"}
43
- ],
44
- "links" => {
45
- "self" => "http://example.com/articles/1/relationships/comments",
46
- "related" => "http://example.com/articles/1/comments"
47
- }
48
- }
49
- },
50
- "links" => {
51
- "self" => "http://example.com/articles/1"
52
- }
53
- }]))
27
+ "id" => "1",
28
+ "type" => "articles",
29
+ "attributes" => {
30
+ "title" => "JSON API paints my bikeshed!"
31
+ },
32
+ "relationships" => {
33
+ "author" => {
34
+ "data" => { "id" => "9", "type" => "people" },
35
+ "links" => {
36
+ "self" => "http://example.com/articles/1/relationships/author",
37
+ "related" => "http://example.com/articles/1/author"
38
+ }
39
+ },
40
+ "comments" => {
41
+ "data" => [
42
+ { "id" => "5", "type" => "comments" },
43
+ { "id" => "12", "type" => "comments" }
44
+ ],
45
+ "links" => {
46
+ "self" => "http://example.com/articles/1/relationships/comments",
47
+ "related" => "http://example.com/articles/1/comments"
48
+ }
49
+ }
50
+ },
51
+ "links" => {
52
+ "self" => "http://example.com/articles/1"
53
+ }
54
+ }]))
54
55
  end
55
56
 
56
57
  it("has a links key at root with pagination") do
57
58
  expect(subject.fetch("links")).to(eq(
58
- "self" => "http://example.com/articles",
59
- "next" => "http://example.com/articles?page[offset]=2&page[limit]=1",
60
- "last" => "http://example.com/articles?page[offset]=3&page[limit]=1"
61
- ))
59
+ "self" => "http://example.com/articles",
60
+ "next" => "http://example.com/articles?page[offset]=2&page[limit]=1",
61
+ "last" => "http://example.com/articles?page[offset]=3&page[limit]=1"
62
+ ))
62
63
  end
63
64
 
64
65
  it("has a included key at root with included models") do
65
66
  expect(subject.fetch("included")).to(include(
66
- {
67
- "id" => "5",
68
- "type" => "comments",
69
- "attributes"=>{"body"=>"First!"},
70
- "relationships" => {
71
- "author" => {"data" => {"id" => "2", "type" => "people"}, "links" => {"self" => "http://example.com/comments/5/relationships/author", "related" => "http://example.com/comments/5/author"}},
72
- "article" => {"data" => {"id" => "1", "type" => "articles"}, "links" => {"self" => "http://example.com/comments/5/relationships/article", "related" => "http://example.com/comments/5/article"}}
73
- },
74
- "links" => {"self" => "http://example.com/comments/5"}
75
- },
76
- {
77
- "id" => "12",
78
- "type" => "comments",
79
- "attributes"=>{"body"=>"I like XML better"},
80
- "relationships" => {
81
- "author" => {"data" => {"id" => "9", "type" => "people"}, "links" => {"self" => "http://example.com/comments/12/relationships/author", "related" => "http://example.com/comments/12/author"}},
82
- "article" => {"data" => {"id" => "1", "type" => "articles"}, "links" => {"self" => "http://example.com/comments/12/relationships/article", "related" => "http://example.com/comments/12/article"}}
83
- },
84
- "links" => {"self" => "http://example.com/comments/12"}
85
- },
86
- {
87
- "id" => "9",
88
- "type" => "people",
89
- "attributes"=>{"name"=>"Dan Gebhardt"},
90
- "relationships" => {
91
- "comments" => {"data" => [{"id" => "12", "type" => "comments"}], "links" => {"self" => "http://example.com/people/9/relationships/comments", "related" => "http://example.com/people/9/comments"}},
92
- "articles" => {
93
- "data" => [{"id" => "1", "type" => "articles"}, {"id" => "2", "type" => "articles"}, {"id" => "3", "type" => "articles"}],
94
- "links" => {"self" => "http://example.com/people/9/relationships/articles", "related" => "http://example.com/people/9/articles"}
95
- }
96
- },
97
- "links" => {"self" => "http://example.com/people/9"}
98
- }
99
- ))
67
+ {
68
+ "id" => "5",
69
+ "type" => "comments",
70
+ "attributes" => { "body" => "First!" },
71
+ "relationships" => {
72
+ "author" => { "data" => { "id" => "2", "type" => "people" }, "links" => { "self" => "http://example.com/comments/5/relationships/author", "related" => "http://example.com/comments/5/author" } },
73
+ "article" => { "data" => { "id" => "1", "type" => "articles" }, "links" => { "self" => "http://example.com/comments/5/relationships/article", "related" => "http://example.com/comments/5/article" } }
74
+ },
75
+ "links" => { "self" => "http://example.com/comments/5" }
76
+ },
77
+ {
78
+ "id" => "12",
79
+ "type" => "comments",
80
+ "attributes" => { "body" => "I like XML better" },
81
+ "relationships" => {
82
+ "author" => { "data" => { "id" => "9", "type" => "people" }, "links" => { "self" => "http://example.com/comments/12/relationships/author", "related" => "http://example.com/comments/12/author" } },
83
+ "article" => { "data" => { "id" => "1", "type" => "articles" }, "links" => { "self" => "http://example.com/comments/12/relationships/article", "related" => "http://example.com/comments/12/article" } }
84
+ },
85
+ "links" => { "self" => "http://example.com/comments/12" }
86
+ },
87
+ {
88
+ "id" => "9",
89
+ "type" => "people",
90
+ "attributes" => { "name" => "Dan Gebhardt" },
91
+ "relationships" => {
92
+ "comments" => { "data" => [{ "id" => "12", "type" => "comments" }], "links" => { "self" => "http://example.com/people/9/relationships/comments", "related" => "http://example.com/people/9/comments" } },
93
+ "articles" => {
94
+ "data" => [{ "id" => "1", "type" => "articles" }, { "id" => "2", "type" => "articles" }, { "id" => "3", "type" => "articles" }],
95
+ "links" => { "self" => "http://example.com/people/9/relationships/articles", "related" => "http://example.com/people/9/articles" }
96
+ }
97
+ },
98
+ "links" => { "self" => "http://example.com/people/9" }
99
+ }
100
+ ))
100
101
  end
101
102
  end
102
103
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  module Materializer
3
5
  class Configuration
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  module Materializer
3
5
  module Controller
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  module Materializer
3
5
  class Error
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  module Materializer
3
5
  class Error
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  module Materializer
3
5
  class Error
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  module Materializer
3
5
  class Error
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  module Materializer
3
5
  class Error < StandardError
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  module Materializer
3
5
  module Resource
@@ -7,12 +9,10 @@ module JSONAPI
7
9
  attr_accessor(:owner)
8
10
  attr_accessor(:name)
9
11
  attr_accessor(:from)
10
- attr_accessor(:visible)
11
12
 
12
13
  validates_presence_of(:owner)
13
14
  validates_presence_of(:name)
14
15
  validates_presence_of(:from)
15
- validate(:visible_callable)
16
16
 
17
17
  def initialize(**keyword_arguments)
18
18
  super(**keyword_arguments)
@@ -24,25 +24,9 @@ module JSONAPI
24
24
  subject.object.public_send(from)
25
25
  end
26
26
 
27
- def visible?(subject)
28
- return visible if [true, false].include?(visible)
29
- return subject.send(visible, self) if visible.is_a?(Symbol)
30
- return visible.call(self) if visible.respond_to?(:call)
31
-
32
- true
33
- end
34
-
35
27
  private def materializer_class
36
28
  class_name.constantize
37
29
  end
38
-
39
- private def visible_callable
40
- return if [true, false].include?(visible)
41
- return if visible.is_a?(Symbol)
42
- return if visible.respond_to?(:call)
43
-
44
- errors.add(:visible, "not callable or boolean")
45
- end
46
30
  end
47
31
  end
48
32
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  module Materializer
3
5
  module Resource
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  module Materializer
3
5
  module Resource
@@ -9,14 +11,12 @@ module JSONAPI
9
11
  attr_accessor(:type)
10
12
  attr_accessor(:from)
11
13
  attr_accessor(:class_name)
12
- attr_accessor(:visible)
13
14
 
14
15
  validates_presence_of(:owner)
15
16
  validates_presence_of(:name)
16
17
  validates_presence_of(:type)
17
18
  validates_presence_of(:from)
18
19
  validates_presence_of(:class_name)
19
- validate(:visible_callable)
20
20
 
21
21
  def initialize(**keyword_arguments)
22
22
  super(**keyword_arguments)
@@ -27,40 +27,28 @@ module JSONAPI
27
27
  def for(subject)
28
28
  @for ||= {}
29
29
  @for[checksum(subject)] ||= case type
30
- when :many then
31
- unlessing(fetch_relation(subject), -> {subject.includes.any? {|included| included.include?(from.to_s)} || fetch_relation(subject).loaded?}) do |subject|
32
- subject.select(:id)
33
- end.map do |related_object|
34
- materializer_class.new(
35
- **subject.raw,
36
- :object => related_object
37
- )
38
- end
39
- when :one then
40
- if fetch_relation(subject).present?
41
- materializer_class.new(
42
- **subject.raw,
43
- :object => fetch_relation(subject)
44
- )
45
- end
46
- end
47
- end
48
-
49
- def visible?(subject)
50
- return visible if [true, false].include?(visible)
51
- return subject.send(visible, self) if visible.is_a?(Symbol)
52
- return visible.call(self) if visible.respond_to?(:call)
53
-
54
- true
55
- end
56
-
57
- private def fetch_relation(subject)
58
- @fetch_relationship ||= {}
59
- @fetch_relationship[checksum(subject)] ||= subject.object.public_send(from)
30
+ when :many
31
+ unlessing(fetch_relation(subject), -> { subject.includes.any? { |included| included.include?(from.to_s) } || fetch_relation(subject).loaded? }) do |subject|
32
+ subject.select(:id)
33
+ end.map do |related_object|
34
+ materializer_class.new(
35
+ **subject.raw,
36
+ object: related_object
37
+ )
38
+ end
39
+ when :one
40
+ if fetch_relation(subject).present?
41
+ materializer_class.new(
42
+ **subject.raw,
43
+ object: fetch_relation(subject)
44
+ )
45
+ end
46
+ end
60
47
  end
48
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
61
49
 
62
50
  def using(parent)
63
- Resource::Relationship.new(:related => self, :parent => parent)
51
+ Resource::Relationship.new(related: self, parent:)
64
52
  end
65
53
 
66
54
  def many?
@@ -71,23 +59,20 @@ module JSONAPI
71
59
  type == :one
72
60
  end
73
61
 
74
- private def materializer_class
75
- class_name.constantize
62
+ private def fetch_relation(subject)
63
+ @fetch_relationship ||= {}
64
+ @fetch_relationship[checksum(subject)] ||= subject.object.public_send(from)
76
65
  end
77
66
 
78
- private def visible_callable
79
- return if [true, false].include?(visible)
80
- return if visible.is_a?(Symbol)
81
- return if visible.respond_to?(:call)
82
-
83
- errors.add(:visible, "not callable or boolean")
67
+ private def materializer_class
68
+ class_name.constantize
84
69
  end
85
70
 
86
71
  private def unlessing(object, proc)
87
- unless proc.call()
88
- yield(object)
89
- else
72
+ if proc.call
90
73
  object
74
+ else
75
+ yield(object)
91
76
  end
92
77
  end
93
78
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSONAPI
2
4
  module Materializer
3
5
  module Resource
@@ -18,12 +20,12 @@ module JSONAPI
18
20
 
19
21
  def as_json(*)
20
22
  {
21
- :data => data,
22
- :links => {
23
- :self => links_self,
24
- :related => links_related
23
+ data:,
24
+ links: {
25
+ self: links_self,
26
+ related: links_related
25
27
  },
26
- :meta => {}
28
+ meta: {}
27
29
  }.transform_values(&:presence).compact
28
30
  end
29
31
 
@@ -33,31 +35,32 @@ module JSONAPI
33
35
  ).pattern
34
36
  end
35
37
 
36
- private def links_related
38
+ def links_related
37
39
  Addressable::Template.new(
38
40
  "#{parent.links_self}/#{related.name}"
39
41
  ).pattern
40
42
  end
41
43
 
42
- private def data
44
+ def data
43
45
  return if related_parent_materializer.blank?
44
46
 
45
47
  @data ||= if related.many?
46
- related_parent_materializer.map do |child|
47
- {
48
- :id => child.attribute("id").for(child).to_s,
49
- :type => child.type.to_s
50
- }
51
- end
52
- else
53
- {
54
- :id => related_parent_materializer.attribute("id").for(related_parent_materializer).to_s,
55
- :type => related_parent_materializer.type.to_s
56
- }
57
- end
48
+ related_parent_materializer.map do |child|
49
+ {
50
+ id: child.attribute("id").for(child).to_s,
51
+ type: child.type.to_s
52
+ }
53
+ end
54
+ else
55
+ {
56
+ id: related_parent_materializer.attribute("id").for(related_parent_materializer).to_s,
57
+ type: related_parent_materializer.type.to_s
58
+ }
59
+ end
58
60
  end
61
+ # rubocop:enable Metrics/AbcSize
59
62
 
60
- private def related_parent_materializer
63
+ def related_parent_materializer
61
64
  related.for(parent)
62
65
  end
63
66
  end