jsonapi-renderer 0.1.3 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 64a6b359e09767d1fb82f5c99e5c00493180fee2
4
- data.tar.gz: 7cbe497431c5af06536206776c5ea4c791085e3c
2
+ SHA256:
3
+ metadata.gz: 221e35b89366443f6a4e545e42b0026b824c0fc7915170354f5e2b4989cd6d76
4
+ data.tar.gz: 22912dac4be273f88d701e07fcc154de4a745962113a07e3bac3f075bec7fb8d
5
5
  SHA512:
6
- metadata.gz: 560dd5946e8a744de498eabc762f1f90f4c834a215a2b57e58adba24b9b5f6d6e1e5b878123195b82c2ec6f58f64468cdb530159cd30e5bb99f226134238d66a
7
- data.tar.gz: 4fdf98ad9829edfc5e254b4df9a7a7f765ee9de05784c76c6f6e35ee73f64b154e75bf40af6953873f0d1cfdeb0dbb096401cc982eac546388930b0219a5741b
6
+ metadata.gz: bbe2e5a3ec1c4b7c2fe25b04f386b8fc792c75faf1f924dba3e647f46962db8ab87d2a575f03622ab5dd3dba84528f0721423f11ad761344e9c5c1527da15716
7
+ data.tar.gz: efa85300205e568b7e4348bfe8f76d182be85948287170fee67f2f2d5220a9aa02a07dd64af7d370e8d921d7eaff05eb6d2e11ba79db453f7e250bcb14ce6415
data/README.md CHANGED
@@ -48,7 +48,7 @@ class ResourceInterface
48
48
  # @return [String]
49
49
  def jsonapi_id; end
50
50
 
51
- # Returns a hash containing, for each included relationship, an array of the
51
+ # Returns a hash containing, for each included relationship, an array of the
52
52
  # resources to be included from that one.
53
53
  # @param included_relationships [Array<Symbol>] The keys of the relationships
54
54
  # to be included.
@@ -57,8 +57,8 @@ class ResourceInterface
57
57
 
58
58
  # Returns a JSON API-compliant representation of the resource as a hash.
59
59
  # @param options [Hash]
60
- # @option fields [Array<Symbol>, Nil] The requested fields, or nil.
61
- # @option include [Array<Symbol>] The requested relationships to
60
+ # @option fields [Set<Symbol>, Nil] The requested fields, or nil.
61
+ # @option include [Set<Symbol>] The requested relationships to
62
62
  # include (defaults to []).
63
63
  # @return [Hash]
64
64
  def as_jsonapi(options = {}); end
@@ -87,6 +87,18 @@ JSONAPI.render(data: resources,
87
87
 
88
88
  This returns a JSON API compliant hash representing the described document.
89
89
 
90
+ #### Rendering a relationship
91
+ ```ruby
92
+ JSONAPI.render(data: resource,
93
+ relationship: :posts,
94
+ include: include_string,
95
+ fields: fields_hash,
96
+ meta: meta_hash,
97
+ links: links_hash)
98
+ ```
99
+
100
+ This returns a JSON API compliant hash representing the described document.
101
+
90
102
  ### Rendering errors
91
103
 
92
104
  ```ruby
@@ -100,6 +112,32 @@ returns a JSON API-compliant representation of the error.
100
112
 
101
113
  This returns a JSON API compliant hash representing the described document.
102
114
 
115
+ ### Caching
116
+
117
+ The generated JSON fragments can be cached in any cache implementation
118
+ supporting the `fetch_multi` method.
119
+
120
+ When using caching, the serializable resources must implement an
121
+ additional `jsonapi_cache_key` method:
122
+ ```ruby
123
+ # Returns a cache key for the resource, parameterized by the `include` and
124
+ # `fields` options.
125
+ # @param options [Hash]
126
+ # @option fields [Set<Symbol>, Nil] The requested fields, or nil.
127
+ # @option include [Set<Symbol>] The requested relationships to
128
+ # include (defaults to []).
129
+ # @return [String]
130
+ def jsonapi_cache_key(options = {}); end
131
+ ```
132
+
133
+ The cache instance must be passed to the renderer as follows:
134
+ ```ruby
135
+ JSONAPI.render(data: resources,
136
+ include: include_string,
137
+ fields: fields_hash,
138
+ cache: cache_instance)
139
+ ```
140
+
103
141
  ## License
104
142
 
105
143
  jsonapi-renderer is released under the [MIT License](http://www.opensource.org/licenses/MIT).
@@ -16,6 +16,8 @@ module JSONAPI
16
16
  def initialize(include_args, options = {})
17
17
  include_hash = Parser.parse_include_args(include_args)
18
18
  @hash = include_hash.each_with_object({}) do |(key, value), hash|
19
+ raise InvalidKey, key unless valid?(key)
20
+
19
21
  hash[key] = self.class.new(value, options)
20
22
  end
21
23
  @options = options
@@ -68,5 +70,18 @@ module JSONAPI
68
70
 
69
71
  string_array.join(',')
70
72
  end
73
+
74
+ class InvalidKey < StandardError; end
75
+
76
+ private
77
+
78
+ def valid?(key)
79
+ key.match(valid_json_key_name_regex)
80
+ end
81
+
82
+ def valid_json_key_name_regex
83
+ # https://jsonapi.org/format/#document-member-names
84
+ /^(?![\s\-_])[\u0080-\u10FFA-Za-z0-9* _-]+(?<![\s\-_])$/
85
+ end
71
86
  end
72
87
  end
@@ -0,0 +1,47 @@
1
+ require 'jsonapi/renderer/resources_processor'
2
+
3
+ module JSONAPI
4
+ class Renderer
5
+ # @private
6
+ class CachedResourcesProcessor < ResourcesProcessor
7
+ class JSONString < String
8
+ def to_json(*)
9
+ self
10
+ end
11
+ end
12
+
13
+ def initialize(cache)
14
+ @cache = cache
15
+ end
16
+
17
+ def process_resources
18
+ # NOTE(beauby): This is necessary for cache keys consistency.
19
+ @include_rels = @include_rels.each_with_object({}) do |(k, v), h|
20
+ h[k] = v.to_a.sort!
21
+ end
22
+
23
+ [@primary, @included].each do |resources|
24
+ cache_hash = cache_key_map(resources)
25
+ processed_resources = @cache.fetch_multi(*cache_hash.keys) do |key|
26
+ res, include, fields = cache_hash[key]
27
+ json = res.as_jsonapi(include: include, fields: fields).to_json
28
+
29
+ JSONString.new(json)
30
+ end
31
+
32
+ resources.replace(processed_resources.values)
33
+ end
34
+ end
35
+
36
+ def cache_key_map(resources)
37
+ resources.each_with_object({}) do |res, h|
38
+ ri = [res.jsonapi_type, res.jsonapi_id]
39
+ include_dir = @include_rels[ri]
40
+ fields = @fields[ri.first.to_sym]
41
+ h[res.jsonapi_cache_key(include: include_dir, fields: fields)] =
42
+ [res, include_dir, fields]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,17 +1,21 @@
1
1
  require 'jsonapi/include_directive'
2
- require 'jsonapi/renderer/resources_processor'
2
+ require 'jsonapi/renderer/simple_resources_processor'
3
+ require 'jsonapi/renderer/cached_resources_processor'
3
4
 
4
5
  module JSONAPI
5
6
  class Renderer
7
+ # @private
6
8
  class Document
7
9
  def initialize(params = {})
8
10
  @data = params.fetch(:data, :no_data)
9
11
  @errors = params.fetch(:errors, [])
10
12
  @meta = params[:meta]
11
13
  @links = params[:links] || {}
12
- @fields = _symbolize_fields(params[:fields] || {})
14
+ @fields = _canonize_fields(params[:fields] || {})
13
15
  @jsonapi = params[:jsonapi]
14
16
  @include = JSONAPI::IncludeDirective.new(params[:include] || {})
17
+ @relationship = params[:relationship]
18
+ @cache = params[:cache]
15
19
  end
16
20
 
17
21
  def to_hash
@@ -21,37 +25,75 @@ module JSONAPI
21
25
 
22
26
  private
23
27
 
28
+ # rubocop:disable Metrics/PerceivedComplexity, Metrics/MethodLength
29
+ # rubocop:disable Metrics/CyclomaticComplexity
24
30
  def document_hash
25
31
  {}.tap do |hash|
26
- if @data != :no_data
32
+ if @relationship
33
+ hash.merge!(relationship_hash)
34
+ elsif @data != :no_data
27
35
  hash.merge!(data_hash)
28
36
  elsif @errors.any?
29
37
  hash.merge!(errors_hash)
30
38
  end
31
- hash[:links] = @links if @links.any?
32
- hash[:meta] = @meta unless @meta.nil?
33
- hash[:jsonapi] = @jsonapi unless @jsonapi.nil?
39
+ hash[:links] = @links if @links.any?
40
+ hash[:meta] = @meta unless @meta.nil?
41
+ hash[:jsonapi] = @jsonapi unless @jsonapi.nil?
34
42
  end
35
43
  end
44
+ # rubocop:enable Metrics/PerceivedComplexity, Metrics/MethodLength
45
+ # rubocop:enable Metrics/CyclomaticComplexity
36
46
 
37
47
  def data_hash
38
48
  primary, included =
39
- ResourcesProcessor.new(Array(@data), @include, @fields).process
49
+ resources_processor.process(Array(@data), @include, @fields)
40
50
  {}.tap do |hash|
41
51
  hash[:data] = @data.respond_to?(:to_ary) ? primary : primary[0]
42
52
  hash[:included] = included if included.any?
43
53
  end
44
54
  end
45
55
 
56
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
57
+ def relationship_hash
58
+ rel_name = @relationship.to_sym
59
+ data = @data.jsonapi_related([rel_name])[rel_name]
60
+ included =
61
+ if @include.key?(rel_name)
62
+ resources_processor.process(data, @include[rel_name], @fields)
63
+ .flatten!
64
+ else
65
+ []
66
+ end
67
+
68
+ res = @data.as_jsonapi(fields: [rel_name], include: [rel_name])
69
+ rel = res[:relationships][rel_name]
70
+ @links = rel[:links].merge!(@links)
71
+ @meta ||= rel[:meta]
72
+
73
+ {}.tap do |hash|
74
+ hash[:data] = rel[:data]
75
+ hash[:included] = included if included.any?
76
+ end
77
+ end
78
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
79
+
46
80
  def errors_hash
47
81
  {}.tap do |hash|
48
- hash[:errors] = @errors.map(&:as_jsonapi)
82
+ hash[:errors] = @errors.flat_map(&:as_jsonapi)
83
+ end
84
+ end
85
+
86
+ def resources_processor
87
+ if @cache
88
+ CachedResourcesProcessor.new(@cache)
89
+ else
90
+ SimpleResourcesProcessor.new
49
91
  end
50
92
  end
51
93
 
52
- def _symbolize_fields(fields)
94
+ def _canonize_fields(fields)
53
95
  fields.each_with_object({}) do |(k, v), h|
54
- h[k.to_sym] = v.map(&:to_sym)
96
+ h[k.to_sym] = v.map(&:to_sym).sort!
55
97
  end
56
98
  end
57
99
  end
@@ -2,14 +2,13 @@ require 'set'
2
2
 
3
3
  module JSONAPI
4
4
  class Renderer
5
+ # @private
5
6
  class ResourcesProcessor
6
- def initialize(resources, include, fields)
7
+ def process(resources, include, fields)
7
8
  @resources = resources
8
9
  @include = include
9
10
  @fields = fields
10
- end
11
11
 
12
- def process
13
12
  traverse_resources
14
13
  process_resources
15
14
 
@@ -73,14 +72,7 @@ module JSONAPI
73
72
  end
74
73
 
75
74
  def process_resources
76
- [@primary, @included].each do |resources|
77
- resources.map! do |res|
78
- ri = [res.jsonapi_type, res.jsonapi_id]
79
- include_dir = @include_rels[ri]
80
- fields = @fields[res.jsonapi_type.to_sym]
81
- res.as_jsonapi(include: include_dir, fields: fields)
82
- end
83
- end
75
+ raise 'Not implemented'
84
76
  end
85
77
  end
86
78
  end
@@ -0,0 +1,19 @@
1
+ require 'jsonapi/renderer/resources_processor'
2
+
3
+ module JSONAPI
4
+ class Renderer
5
+ # @api private
6
+ class SimpleResourcesProcessor < ResourcesProcessor
7
+ def process_resources
8
+ [@primary, @included].each do |resources|
9
+ resources.map! do |res|
10
+ ri = [res.jsonapi_type, res.jsonapi_id]
11
+ include_dir = @include_rels[ri]
12
+ fields = @fields[res.jsonapi_type.to_sym]
13
+ res.as_jsonapi(include: include_dir, fields: fields)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi-renderer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Hosseini
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-12 00:00:00.000000000 Z
11
+ date: 2019-06-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -39,19 +39,19 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.5'
41
41
  - !ruby/object:Gem::Dependency
42
- name: codecov
42
+ name: simplecov
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '0.1'
47
+ version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - "~>"
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '0.1'
54
+ version: '0'
55
55
  description: Efficiently render JSON API documents.
56
56
  email: lucas.hosseini@gmail.com
57
57
  executables: []
@@ -62,8 +62,10 @@ files:
62
62
  - lib/jsonapi/include_directive.rb
63
63
  - lib/jsonapi/include_directive/parser.rb
64
64
  - lib/jsonapi/renderer.rb
65
+ - lib/jsonapi/renderer/cached_resources_processor.rb
65
66
  - lib/jsonapi/renderer/document.rb
66
67
  - lib/jsonapi/renderer/resources_processor.rb
68
+ - lib/jsonapi/renderer/simple_resources_processor.rb
67
69
  homepage: https://github.com/jsonapi-rb/jsonapi-renderer
68
70
  licenses:
69
71
  - MIT
@@ -84,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
86
  version: '0'
85
87
  requirements: []
86
88
  rubyforge_project:
87
- rubygems_version: 2.6.12
89
+ rubygems_version: 2.7.8
88
90
  signing_key:
89
91
  specification_version: 4
90
92
  summary: Render JSONAPI documents.