jsonapi_for_rails 0.1.7 → 0.2.0.pre

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
2
  SHA1:
3
- metadata.gz: 06227ce867a3b98abeac078d6dd809d9da6791a9
4
- data.tar.gz: ff84fc17fff124f48a269c128f8aece931363b94
3
+ metadata.gz: da54359b12e07d61046ce9a96b2b0c8edadb2af2
4
+ data.tar.gz: bef39399515bd6b7b6a10d5466d26d45c60807ec
5
5
  SHA512:
6
- metadata.gz: 5cb2103dea40467f6ded25d379c2c41a1696258e3630659c225b005f553480c015f07e0beb37149760ffe99422334d21050656d605d3e7b76bcf21a39f6585e9
7
- data.tar.gz: 97c37f8bc2bf07732c0c254f1231594a1ea595202ec0191f42a8eff97c2fc975f6f8e12c1f704b09fe4cb855042e1807ab8ca949af8a0d6a4b8d7a3dc5b5d76b
6
+ metadata.gz: 5e422031d14cea9d91d30591ceafb5616f868b6cd4471c34a705a7ae7837c9aabc4c74acb6627efaa78f7e311b7a6ec640d9b35fe2533ccd419fc1d1d1acf34a
7
+ data.tar.gz: 5750ba36ebe04753855d98914a1fa700f23db4da0541a97d7551b9821c89713a256264f2c9d34fc0400d8a4c67c7f49e47a32d7b187db4e0f933e41692bbe0ee
checksums.yaml.gz.sig CHANGED
Binary file
data/README.md CHANGED
@@ -34,7 +34,7 @@ $
34
34
  $ # Check the used version
35
35
  $ bin/rails console
36
36
  irb(main):001:0> JsonapiForRails::VERSION
37
- => "0.1.7"
37
+ => "0.2.0.pre"
38
38
  irb(main):002:0> exit
39
39
  $
40
40
  ```
@@ -70,6 +70,12 @@ class ApplicationController < ActionController::Base # or ActionController::API
70
70
  end
71
71
  ```
72
72
 
73
+ `acts_as_jsonapi_resources` accepts the following keyword arguments:
74
+
75
+ * `content_negotiation`: Set this to `false` to disable
76
+ [content negotiation](http://jsonapi.org/format/1.0/#content-negotiation).
77
+ Default value is `true`. Usage example: `acts_as_jsonapi_resources content_negotiation: false`
78
+
73
79
  If only some of your controllers are JSONAPI controllers, then create a parent controller for only those controllers, and enable JSONAPI inside that controller rather than `ApplicationController`.
74
80
 
75
81
  ```bash
@@ -128,7 +134,7 @@ After populating your database and launching the built-in Rails server with the
128
134
 
129
135
  ```bash
130
136
  $ # Get the list of articles
131
- $ # (the response body is prettified when the Rails environment is 'development' or 'test')
137
+ $ # (the returned HTTP response body is short and terse, but it is prettified here for legibility)
132
138
  $ curl 'http://localhost:3000/api/v1/articles'
133
139
  {
134
140
  "data": [
@@ -142,7 +148,6 @@ $ curl 'http://localhost:3000/api/v1/articles'
142
148
  }
143
149
  ]
144
150
  }
145
- $
146
151
  $ # Get an article
147
152
  $ curl 'http://localhost:3000/api/v1/articles/618037523'
148
153
  {
@@ -165,7 +170,6 @@ $ curl 'http://localhost:3000/api/v1/articles/618037523'
165
170
  }
166
171
  }
167
172
  }
168
- $
169
173
  $ # Get only the title and author of an article, include the author's name
170
174
  $ curl 'http://localhost:3000/api/v1/articles/618037523?filter%5Barticles%5D=title,author;include=author;filter%5Bauthors%5D=name'
171
175
  {
@@ -274,7 +278,8 @@ class ArticlesController < JsonapiResourcesController
274
278
  # @jsonapi_record contains the current database record.
275
279
  # It is available inside all action methods (including all relationship
276
280
  # methods) except :index and :create.
277
- @jsonapi_record.to_jsonapi_hash # => {data: {...}}
281
+ @jsonapi_record.to_jsonapi_hash # => {data: {...}}
282
+ @jsonapi_record.to_jsonapi_errors_hash # => {errors: [...]}
278
283
 
279
284
  # ...
280
285
  end
@@ -305,6 +310,7 @@ The internal architecture is sound. Test coverage is currently being bulked up u
305
310
 
306
311
  Feature support roundup:
307
312
 
313
+ * [Content negotiation](http://jsonapi.org/format/1.0/#content-negotiation) is not implemented.
308
314
  * [Inclusion of related resources](http://jsonapi.org/format/1.0/#fetching-includes) is currently only implemented for requests that return a single resource, and relationship paths are not supported.
309
315
  * [Sparse fieldsets](http://jsonapi.org/format/1.0/#fetching-sparse-fieldsets) is currently only implemented for requests that return a single resource.
310
316
  * [Sorting](http://jsonapi.org/format/1.0/#fetching-sorting) is currently not implemented.
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # Rakefile format: http://rake.rubyforge.org/files/doc/rakefile_rdoc.html
2
+
1
3
  begin
2
4
  require 'bundler/setup'
3
5
  rescue LoadError
@@ -22,8 +22,9 @@ module JsonapiForRails::Controller
22
22
 
23
23
  # implements Create and Update operations
24
24
  def create
25
+
26
+ # attributes
25
27
  begin
26
- # attributes
27
28
  attrs = received_attributes
28
29
  if attrs
29
30
  if @jsonapi_record
@@ -31,38 +32,42 @@ module JsonapiForRails::Controller
31
32
  @jsonapi_record.update! attrs
32
33
  else
33
34
  # create
34
- @jsonapi_record = jsonapi_model_class.create! attrs
35
- end
36
- end
37
-
38
- # relationships
39
- received_relationships.each do |relationship|
40
- # to-one
41
- if relationship[:definition][:type] == :to_one
42
- @jsonapi_record.send :"#{relationship[:definition][:name]}=", relationship[:params][:data]
43
- next
44
- end
45
-
46
- # to-many
47
- @jsonapi_record.send(relationship[:definition][:name]).send :clear # initialize the relation
48
-
49
- relationship[:params][:data].each do |item|
50
- object = relationship[:receiver][:class].find_by_id item[:id]
51
- @jsonapi_record.send(relationship[:definition][:name]).send :<<, object
35
+ @jsonapi_record = jsonapi_model_class.new attrs
36
+ @jsonapi_record.save!
52
37
  end
53
38
  end
54
39
  rescue NameError => e
55
-
56
- # error when creating record
57
- render_error 500, "Model class not found."
40
+ render_errors 500, "Model class not found."
58
41
  return
59
42
  rescue
60
- # error when creating relationship?
61
- @jsonapi_record.destroy if @jsonapi_record
62
-
63
- render_error 500, "Record could not be created."
43
+ render_errors 500, @jsonapi_record.to_jsonapi_errors_hash
64
44
  return
65
45
  end
46
+
47
+ # relationships
48
+ received_relationships.each do |relationship|
49
+ begin
50
+ # to-one
51
+ if relationship[:definition][:type] == :to_one
52
+ @jsonapi_record.send :"#{relationship[:definition][:name]}=", relationship[:params][:data]
53
+ next
54
+ end
55
+
56
+ # to-many
57
+ @jsonapi_record.send(relationship[:definition][:name]).send :clear # initialize the relation
58
+
59
+ relationship[:params][:data].each do |item|
60
+ object = relationship[:receiver][:class].find_by_id item[:id]
61
+ @jsonapi_record.send(relationship[:definition][:name]).send :<<, object
62
+ end
63
+
64
+ rescue
65
+ # Should not happen
66
+ render_errors 500, "Relationship could not be created."
67
+ return
68
+ end
69
+ end
70
+
66
71
  show
67
72
  end
68
73
 
@@ -108,7 +113,7 @@ module JsonapiForRails::Controller
108
113
  end
109
114
 
110
115
  def destroy
111
- render_error 500, "Not implemented."
116
+ render_errors 500, "Not implemented."
112
117
  end
113
118
 
114
119
  # private
@@ -35,7 +35,7 @@ module JsonapiForRails::Controller
35
35
  # PATCH
36
36
  def relationship_update
37
37
  if @jsonapi_relationship[:definition][:type] == :to_many
38
- render_error 403, 'Replacing all members of a to-many relationship is forbidden.'
38
+ render_errors 403, 'Replacing all members of a to-many relationship is forbidden.'
39
39
  return
40
40
  end
41
41
 
@@ -45,7 +45,7 @@ module JsonapiForRails::Controller
45
45
  @jsonapi_relationship[:params][:data][:id]
46
46
  )
47
47
  unless related
48
- render_error 403, 'Record not found.'
48
+ render_errors 403, 'Record not found.'
49
49
  return
50
50
  end
51
51
  end
@@ -57,7 +57,7 @@ module JsonapiForRails::Controller
57
57
  # POST for to-many relations only
58
58
  def relationship_add
59
59
  unless @jsonapi_relationship[:definition][:type] == :to_many
60
- render_error 403, 'Operation allowed for to-many relationships only.'
60
+ render_errors 403, 'Operation allowed for to-many relationships only.'
61
61
  return
62
62
  end
63
63
 
@@ -66,7 +66,7 @@ module JsonapiForRails::Controller
66
66
  record[:id]
67
67
  )
68
68
  unless record
69
- render_error 403, "Non-existing record #{record.inspect}."
69
+ render_errors 403, "Non-existing record #{record.inspect}."
70
70
  return
71
71
  end
72
72
  record
@@ -80,7 +80,7 @@ module JsonapiForRails::Controller
80
80
  # DELETE for to-many relations only
81
81
  def relationship_remove
82
82
  unless @jsonapi_relationship[:definition][:type] == :to_many
83
- render_error 403, 'Operation allowed for to-many relationships only.'
83
+ render_errors 403, 'Operation allowed for to-many relationships only.'
84
84
  return
85
85
  end
86
86
 
@@ -89,7 +89,7 @@ module JsonapiForRails::Controller
89
89
  record[:id]
90
90
  )
91
91
  unless record
92
- render_error 403, "Non-existing record #{record.inspect}."
92
+ render_errors 403, "Non-existing record #{record.inspect}."
93
93
  return
94
94
  end
95
95
  record
@@ -11,17 +11,62 @@ module JsonapiForRails::Controller
11
11
 
12
12
  def self.run_macros receiver
13
13
  receiver.instance_exec do
14
- before_action :content_negotiation
15
- private :content_negotiation
14
+ before_action :jsonapi_content_negotiation, only: [
15
+ :create,
16
+ :update,
17
+
18
+ :relationship_update,
19
+ :relationship_add,
20
+ :relationship_remove
21
+ ]
22
+
23
+ private :jsonapi_content_negotiation
16
24
  end
17
25
  end
18
26
 
19
27
  module InstanceMethods
20
- def content_negotiation
21
- #$stderr.puts "#{request.headers}"
22
- # TODO: content negotiation
23
- return
24
- render_error 401, "Bad request."
28
+ def jsonapi_content_negotiation
29
+ jsonapi = ::JsonapiForRails::Controller::Utils::Render::JSONAPI
30
+
31
+ # Verify request's Content-Type header #######################
32
+ content_type = request.headers['Content-Type']
33
+ if content_type.nil? or content_type.strip != jsonapi[:content_type]
34
+ # TODO: DEPRECATION WARNING: `:nothing` option is deprecated and will be removed in Rails 5.1. Use `head` method to respond with empty response body.
35
+ render status: :unsupported_media_type, nothing: true # 415
36
+ return
37
+ end
38
+
39
+ # Verify request's Accept header #############################
40
+ loop do
41
+ # Request must have Accept header
42
+ accept = request.headers['Accept']
43
+ break unless accept
44
+
45
+ # Accept header's media ranges must match the JSONAPI media type
46
+ acceptable = false
47
+ jsonapi[:media_ranges].each do |media_range|
48
+ index = accept.index media_range
49
+ next unless index
50
+
51
+ acceptable = true
52
+
53
+ # Generic media range match?
54
+ next unless media_range == jsonapi[:content_type]
55
+
56
+ # JSONAPI media type match
57
+ if accept[(index+media_range.size)..-1] =~ /^\s*;/
58
+ # Media type parameter detected
59
+ acceptable = false
60
+ break
61
+ end
62
+ end
63
+ break unless acceptable
64
+
65
+ return
66
+ end
67
+
68
+ # TODO: DEPRECATION WARNING: `:nothing` option is deprecated and will be removed in Rails 5.1. Use `head` method to respond with empty response body.
69
+ render :not_acceptable, nothing: true # 406
25
70
  end
26
71
 
27
72
  end
@@ -38,7 +38,7 @@ module JsonapiForRails::Controller
38
38
  end
39
39
  #$stderr.puts "@jsonapi_record: #{@jsonapi_record.inspect}"
40
40
  return if @jsonapi_record
41
- render_error 401, "Bad request."
41
+ render_errors 401, "Bad request."
42
42
  end
43
43
 
44
44
  end
@@ -26,7 +26,7 @@ module JsonapiForRails::Controller
26
26
  @jsonapi_relationship = received_relationships.first
27
27
  return if @jsonapi_relationship
28
28
 
29
- render_error 401, "Bad request."
29
+ render_errors 401, "Bad request."
30
30
  end
31
31
  end
32
32
  end
@@ -11,50 +11,69 @@ module JsonapiForRails::Controller
11
11
 
12
12
  JSONAPI = {
13
13
  specification: 'http://jsonapi.org/format/',
14
- content_type: 'application/vnd.api+json'
14
+ content_type: 'application/vnd.api+json',
15
+ media_range: [
16
+ '*/*',
17
+ 'application/*',
18
+ 'application/vnd.api+json'
19
+ ]
15
20
  }.freeze
16
21
 
17
22
  def self.run_macros receiver
18
23
  receiver.instance_exec do
19
- private :render_json, :render_error
24
+ private :render_json, :render_errors
20
25
  end
21
26
  end
22
27
 
23
28
  module InstanceMethods
24
29
  def render_json object
25
- unless object
26
- render_error 500, 'No message specified.'
27
- return
28
- end
29
-
30
+ # Status code
30
31
  @jsonapi_status = 200
31
- @jsonapi_json = ['development', 'test'].include?(Rails.env) ? JSON.pretty_generate(object) : JSON.generate(object)
32
- @jsonapi_content_type = JSONAPI[:content_type]
33
32
 
33
+ # Generate json
34
+ @jsonapi_json = JSON.generate(object)
35
+
36
+ # Render
34
37
  render(
35
38
  plain: @jsonapi_json,
36
- #json: @jsonapi_json,
37
- status: @jsonapi_status,
38
- content_type: @jsonapi_content_type
39
+ status: @jsonapi_status
39
40
  )
41
+
42
+ # Set content type
43
+ @jsonapi_content_type = JSONAPI[:content_type]
44
+ response.headers['Content-Type'] = @jsonapi_content_type
40
45
  end
41
46
 
42
- def render_error status, title
47
+ def render_errors status, argument
48
+ # Status code
43
49
  @jsonapi_status = status
44
- object = {
45
- errors: [
46
- {title: title}
47
- ]
48
- }
49
- @jsonapi_json = ['development', 'test'].include?(Rails.env) ? JSON.pretty_generate(object) : JSON.generate(object)
50
- @jsonapi_content_type = JSONAPI[:content_type]
51
50
 
51
+ # Generate json
52
+ if argument.kind_of? Hash
53
+ message = argument
54
+ elsif argument.kind_of? Array
55
+ message = {
56
+ errors: argument
57
+ }
58
+ else
59
+ message = {
60
+ errors: [
61
+ {detail: argument.to_s}
62
+ ]
63
+ }
64
+ end
65
+
66
+ @jsonapi_json = JSON.generate(message)
67
+
68
+ # Render
52
69
  render(
53
70
  plain: @jsonapi_json,
54
- #json: @jsonapi_json,
55
- status: @jsonapi_status,
56
- content_type: @jsonapi_content_type
71
+ status: @jsonapi_status
57
72
  )
73
+
74
+ # Set content type
75
+ @jsonapi_content_type = JSONAPI[:content_type]
76
+ response.headers['Content-Type'] = @jsonapi_content_type
58
77
  end
59
78
  end
60
79
 
@@ -17,12 +17,12 @@ module JsonapiForRails::Controller
17
17
  end
18
18
 
19
19
  class_methods do
20
- def acts_as_jsonapi_resources model: nil
20
+ def acts_as_jsonapi_resources content_negotiation: true #, model: nil
21
21
  #$stderr.puts "JsonapiForRails::Controller macro called from #{self}:\n acts_as_jsonapi_resources(model: #{model or 'nil'})"
22
22
 
23
23
  include JsonapiForRails::Controller::Utils::Model
24
24
  include JsonapiForRails::Controller::Utils::Render
25
- include JsonapiForRails::Controller::BeforeActions::ContentNegotiation
25
+ include JsonapiForRails::Controller::BeforeActions::ContentNegotiation if content_negotiation
26
26
  include JsonapiForRails::Controller::BeforeActions::SparseFieldsets
27
27
  include JsonapiForRails::Controller::BeforeActions::Include
28
28
  include JsonapiForRails::Controller::BeforeActions::Record
@@ -25,6 +25,7 @@ module JsonapiForRails::Model
25
25
  if sparse_fieldset
26
26
  next unless sparse_fieldset.find{|f| association.name == f}
27
27
  end
28
+
28
29
  relationship = {}
29
30
  relationships[association.name] = relationship
30
31
 
@@ -47,7 +48,6 @@ module JsonapiForRails::Model
47
48
 
48
49
  # to-one relationship
49
50
  elsif [
50
-
51
51
  ActiveRecord::Reflection::HasOneReflection,
52
52
  ActiveRecord::Reflection::BelongsToReflection
53
53
  ].include? association.class
@@ -66,25 +66,35 @@ module JsonapiForRails::Model
66
66
 
67
67
  # message
68
68
  {
69
- =begin
70
- meta: {
71
- generated_by_class: "#{self.class}"
72
- },
73
- =end
74
69
  data: {
75
- type: jsonapi_model_type,
76
- id: self.id.to_s,
77
-
78
- attributes: attrs,
79
-
70
+ type: jsonapi_model_type,
71
+ id: self.id.to_s,
72
+ attributes: attrs,
80
73
  relationships: relationships
81
74
  }
82
75
  }
83
76
 
84
77
  end
85
78
 
79
+ def to_jsonapi_errors_hash
80
+ {
81
+ errors: errors.messages.to_a.reduce([]){ |result, key_value|
82
+ attribute = key_value.first
83
+ messages = key_value.last
84
+ result + messages.map{ |message|
85
+ {
86
+ detail: message,
87
+ source: {
88
+ pointer: "/data/attributes/#{attribute}",
89
+ }
90
+ }
91
+ }
92
+ }
93
+ }
94
+ end
95
+
86
96
  def jsonapi_model_type
87
- "#{self.class}".underscore.pluralize.to_sym
97
+ self.class.to_s.underscore.pluralize.to_sym
88
98
  end
89
99
 
90
100
  private :jsonapi_model_type
@@ -1,3 +1,4 @@
1
1
  module JsonapiForRails
2
- VERSION = '0.1.7'
2
+ VERSION = '0.2.0.pre'
3
+ # implement content negotiation (http://jsonapi.org/format/1.0/#content-negotiation), add content_negotiation keyword argument to acts_as_jasonapi_resources
3
4
  end
@@ -1,11 +1,14 @@
1
- # TODO: JSONAPI: adding optional members to documents ('jsonapi', 'meta')
2
- # TODO: JSONAPI: return conformant HTTP status codes
3
- # TODO: JSONAPI: conformant and rich 'errors' member
4
- # TODO: JSONAPI: content negotiation
5
- # TODO: JSONAPI: apply sparse fieldsets _after_ including related resources
6
- # TODO: JSONAPI: should included resources list their relationships (which links to primary object)?
7
- # TODO: JSONAPI: do not support Client-Generated IDs?
8
- # TODO: JSONAPI: Location header
1
+
2
+ # TODO: content negotiation tests
3
+ # TODO: apply sparse fieldsets _after_ including related resources
4
+ # TODO: Location header
5
+ # TODO: add 'links' to documents
6
+ # TODO: return conformant HTTP status codes
7
+ # TODO: options for 'acts_as_jsonapi_resources'? example: {content_negotiation: false}
8
+ # TODO: adding optional members to documents ('jsonapi', 'meta')
9
+ # TODO: conformant and rich 'errors' member
10
+ # TODO: should included resources list their relationships (which links to primary object)?
11
+ # TODO: do not support Client-Generated IDs?
9
12
 
10
13
  # TODO: README.md: double-check the installation instructions
11
14
  # TODO: README.md: describe @jsonapi_include ?
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi_for_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.0.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Doga Armangil
@@ -31,7 +31,7 @@ cert_chain:
31
31
  DcNXARPdnPlz0VQedZo89pKjngah8Nur01/wD97Q18Mr/av3H4Bg1sTF+RZAplU3
32
32
  RX+xYPJjPMyjrD73KZ5UNtXo5jOP1T85SqXOl6ICfedTVgZMk9mzK8+PkP1croHi
33
33
  -----END CERTIFICATE-----
34
- date: 2016-03-02 00:00:00.000000000 Z
34
+ date: 2016-03-11 00:00:00.000000000 Z
35
35
  dependencies:
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rails
@@ -95,12 +95,7 @@ files:
95
95
  homepage: https://github.com/doga/jsonapi_for_rails
96
96
  licenses:
97
97
  - MIT
98
- metadata:
99
- code: https://github.com/doga/jsonapi_for_rails
100
- docs: https://github.com/doga/jsonapi_for_rails#jsonapiforrails
101
- wiki: ''
102
- mail: ''
103
- bugs: https://github.com/doga/jsonapi_for_rails/issues
98
+ metadata: {}
104
99
  post_install_message:
105
100
  rdoc_options: []
106
101
  require_paths:
@@ -112,9 +107,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
112
107
  version: '2.0'
113
108
  required_rubygems_version: !ruby/object:Gem::Requirement
114
109
  requirements:
115
- - - ">="
110
+ - - ">"
116
111
  - !ruby/object:Gem::Version
117
- version: '0'
112
+ version: 1.3.1
118
113
  requirements: []
119
114
  rubyforge_project:
120
115
  rubygems_version: 2.5.1
metadata.gz.sig CHANGED
Binary file