jsonapi-renderer 0.1.3 → 0.2.2

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