jsonapi_compliable 0.11.34 → 1.0.alpha.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.
Files changed (73) hide show
  1. checksums.yaml +5 -5
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +1 -2
  4. data/Rakefile +7 -3
  5. data/jsonapi_compliable.gemspec +7 -3
  6. data/lib/generators/jsonapi/resource_generator.rb +8 -79
  7. data/lib/generators/jsonapi/templates/application_resource.rb.erb +2 -1
  8. data/lib/generators/jsonapi/templates/controller.rb.erb +19 -64
  9. data/lib/generators/jsonapi/templates/resource.rb.erb +5 -47
  10. data/lib/generators/jsonapi/templates/resource_reads_spec.rb.erb +62 -0
  11. data/lib/generators/jsonapi/templates/resource_writes_spec.rb.erb +63 -0
  12. data/lib/jsonapi_compliable.rb +87 -18
  13. data/lib/jsonapi_compliable/adapters/abstract.rb +202 -45
  14. data/lib/jsonapi_compliable/adapters/active_record.rb +6 -130
  15. data/lib/jsonapi_compliable/adapters/active_record/base.rb +247 -0
  16. data/lib/jsonapi_compliable/adapters/active_record/belongs_to_sideload.rb +17 -0
  17. data/lib/jsonapi_compliable/adapters/active_record/has_many_sideload.rb +17 -0
  18. data/lib/jsonapi_compliable/adapters/active_record/has_one_sideload.rb +17 -0
  19. data/lib/jsonapi_compliable/adapters/active_record/inferrence.rb +12 -0
  20. data/lib/jsonapi_compliable/adapters/active_record/many_to_many_sideload.rb +30 -0
  21. data/lib/jsonapi_compliable/adapters/null.rb +177 -6
  22. data/lib/jsonapi_compliable/base.rb +33 -320
  23. data/lib/jsonapi_compliable/context.rb +16 -0
  24. data/lib/jsonapi_compliable/deserializer.rb +14 -39
  25. data/lib/jsonapi_compliable/errors.rb +227 -24
  26. data/lib/jsonapi_compliable/extensions/extra_attribute.rb +3 -1
  27. data/lib/jsonapi_compliable/filter_operators.rb +25 -0
  28. data/lib/jsonapi_compliable/hash_renderer.rb +57 -0
  29. data/lib/jsonapi_compliable/query.rb +190 -202
  30. data/lib/jsonapi_compliable/rails.rb +12 -6
  31. data/lib/jsonapi_compliable/railtie.rb +64 -0
  32. data/lib/jsonapi_compliable/renderer.rb +60 -0
  33. data/lib/jsonapi_compliable/resource.rb +35 -663
  34. data/lib/jsonapi_compliable/resource/configuration.rb +239 -0
  35. data/lib/jsonapi_compliable/resource/dsl.rb +138 -0
  36. data/lib/jsonapi_compliable/resource/interface.rb +32 -0
  37. data/lib/jsonapi_compliable/resource/polymorphism.rb +68 -0
  38. data/lib/jsonapi_compliable/resource/sideloading.rb +102 -0
  39. data/lib/jsonapi_compliable/resource_proxy.rb +127 -0
  40. data/lib/jsonapi_compliable/responders.rb +19 -0
  41. data/lib/jsonapi_compliable/runner.rb +25 -0
  42. data/lib/jsonapi_compliable/scope.rb +37 -79
  43. data/lib/jsonapi_compliable/scoping/extra_attributes.rb +29 -0
  44. data/lib/jsonapi_compliable/scoping/filter.rb +39 -58
  45. data/lib/jsonapi_compliable/scoping/filterable.rb +9 -14
  46. data/lib/jsonapi_compliable/scoping/paginate.rb +9 -3
  47. data/lib/jsonapi_compliable/scoping/sort.rb +16 -4
  48. data/lib/jsonapi_compliable/sideload.rb +221 -347
  49. data/lib/jsonapi_compliable/sideload/belongs_to.rb +34 -0
  50. data/lib/jsonapi_compliable/sideload/has_many.rb +16 -0
  51. data/lib/jsonapi_compliable/sideload/has_one.rb +9 -0
  52. data/lib/jsonapi_compliable/sideload/many_to_many.rb +24 -0
  53. data/lib/jsonapi_compliable/sideload/polymorphic_belongs_to.rb +108 -0
  54. data/lib/jsonapi_compliable/stats/payload.rb +4 -8
  55. data/lib/jsonapi_compliable/types.rb +172 -0
  56. data/lib/jsonapi_compliable/util/attribute_check.rb +88 -0
  57. data/lib/jsonapi_compliable/util/persistence.rb +29 -7
  58. data/lib/jsonapi_compliable/util/relationship_payload.rb +4 -4
  59. data/lib/jsonapi_compliable/util/render_options.rb +4 -32
  60. data/lib/jsonapi_compliable/util/serializer_attributes.rb +98 -0
  61. data/lib/jsonapi_compliable/util/validation_response.rb +15 -9
  62. data/lib/jsonapi_compliable/version.rb +1 -1
  63. metadata +105 -24
  64. data/lib/generators/jsonapi/field_generator.rb +0 -0
  65. data/lib/generators/jsonapi/templates/create_request_spec.rb.erb +0 -29
  66. data/lib/generators/jsonapi/templates/destroy_request_spec.rb.erb +0 -20
  67. data/lib/generators/jsonapi/templates/index_request_spec.rb.erb +0 -22
  68. data/lib/generators/jsonapi/templates/payload.rb.erb +0 -39
  69. data/lib/generators/jsonapi/templates/serializer.rb.erb +0 -25
  70. data/lib/generators/jsonapi/templates/show_request_spec.rb.erb +0 -19
  71. data/lib/generators/jsonapi/templates/update_request_spec.rb.erb +0 -33
  72. data/lib/jsonapi_compliable/adapters/active_record_sideloading.rb +0 -152
  73. data/lib/jsonapi_compliable/scoping/extra_fields.rb +0 -58
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 2b577a79ded38bd4613b559334071632b188d2b78f14cc0cb882b5375a37b5a4
4
- data.tar.gz: 49f8349533f4dcd5be4344b4bf54997eaef316cd279611ca830e323ee421f9e0
2
+ SHA1:
3
+ metadata.gz: b893a4986018fbc20f3accdf20fe6b3ad84b6574
4
+ data.tar.gz: 7dc27ab003bde762c655255dec3f4800f88c144d
5
5
  SHA512:
6
- metadata.gz: 5fb80bd50abadabaab56dabc4fb9f20bd2638e2da29cea7b51e5040a388d15f0ce02c2df0dbcd70d7d01b428d4de15fdfd7134a872f45da197e5a39f55776829
7
- data.tar.gz: 00f3c45f9deb647c227d575d17589b6354025b415e7012c96722f3069eccebd2829cebcfd37987f4180e433826835039aaf3b1b82dabd5634bd760a20c7bafef
6
+ metadata.gz: cbf873e797bdd2ff35e4ae5ceed8f8c42f03829c89992cca7a62468210e0f677584d4b0080da6a99e585e47c650d9ffe5386079959117753d96bf51c84e13bb7
7
+ data.tar.gz: dd59e9bb8e8d5c58d8308300647aeefd6c6bb3356fd37b7ddccff3fb98eddc700fc2b92932aede85d8a73f001cc59aa9255186d465ab664c0f28ef7b159039f1
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.5.2
1
+ 2.3.0
data/.travis.yml CHANGED
@@ -1,8 +1,7 @@
1
+ sudo: false
1
2
  language: ruby
2
3
  rvm:
3
4
  - 2.3.3
4
- dist: xenial
5
- os: linux
6
5
 
7
6
  script: "bundle exec rake"
8
7
 
data/Rakefile CHANGED
@@ -2,10 +2,14 @@ require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
  require "appraisal"
4
4
 
5
- RSpec::Core::RakeTask.new(:spec)
5
+ RSpec::Core::RakeTask.new(:spec) do |t|
6
+ if ENV["APPRAISAL_INITIALIZED"]
7
+ t.pattern = 'spec/integration/rails'
8
+ end
9
+ end
6
10
 
7
11
  if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
8
- task :default => :appraisal
12
+ task :default => [:spec, :appraisal]
9
13
  else
10
- task :default => :spec
14
+ task :default => [:spec]
11
15
  end
@@ -6,8 +6,8 @@ require 'jsonapi_compliable/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "jsonapi_compliable"
8
8
  spec.version = JsonapiCompliable::VERSION
9
- spec.authors = ["Lee Richmond", "Venkata Pasupuleti"]
10
- spec.email = ["richmolj@gmail.com", "spasupuleti4@bloomberg.net"]
9
+ spec.authors = ["Lee Richmond"]
10
+ spec.email = ["richmolj@gmail.com"]
11
11
 
12
12
  spec.summary = %q{Easily build jsonapi.org-compatible APIs}
13
13
  spec.license = "MIT"
@@ -19,11 +19,15 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  # Pinning this version until backwards-incompatibility is addressed
21
21
  spec.add_dependency 'jsonapi-serializable', '~> 0.3.0'
22
+ spec.add_dependency 'dry-types', '~> 0.13'
23
+ spec.add_dependency 'jsonapi_errorable', '~> 0.9'
22
24
 
23
25
  spec.add_development_dependency "activerecord", ['>= 4.1', '< 6']
24
26
  spec.add_development_dependency "kaminari", '~> 0.17'
25
- spec.add_development_dependency "bundler", "~> 2.1.4"
27
+ spec.add_development_dependency "bundler", "~> 1.12"
26
28
  spec.add_development_dependency "rake", "~> 10.0"
27
29
  spec.add_development_dependency "sqlite3"
28
30
  spec.add_development_dependency "database_cleaner"
31
+ spec.add_development_dependency "activemodel", ['>= 4.1', '< 6']
32
+ spec.add_development_dependency "jsonapi_spec_helpers", '>= 1.0.alpha.1'
29
33
  end
@@ -22,18 +22,10 @@ module Jsonapi
22
22
  end
23
23
 
24
24
  generate_controller
25
- generate_serializer
26
25
  generate_application_resource unless application_resource_defined?
27
- generate_spec_payload
28
-
29
- if actions?('create', 'update')
30
- generate_strong_resource
31
- end
32
-
33
26
  generate_route
34
27
  generate_tests
35
28
  generate_resource
36
- generate_swagger if docs_controller?
37
29
  end
38
30
 
39
31
  private
@@ -55,11 +47,6 @@ module Jsonapi
55
47
  template('controller.rb.erb', to)
56
48
  end
57
49
 
58
- def generate_serializer
59
- to = File.join('app/serializers', class_path, "serializable_#{file_name}.rb")
60
- template('serializer.rb.erb', to)
61
- end
62
-
63
50
  def generate_application_resource
64
51
  to = File.join('app/resources', class_path, "application_resource.rb")
65
52
  template('application_resource.rb.erb', to)
@@ -69,39 +56,6 @@ module Jsonapi
69
56
  'ApplicationResource'.safe_constantize.present?
70
57
  end
71
58
 
72
- def docs_controller?
73
- File.exists?('app/controllers/docs_controller.rb')
74
- end
75
-
76
- def generate_swagger
77
- code = " jsonapi_resource '/v1/#{type}'"
78
- code << ", only: [#{actions.map { |a| ":#{a}" }.join(', ')}]" if actions.length < 5
79
- code << "\n"
80
- inject_into_file 'app/controllers/docs_controller.rb', before: /^end/ do
81
- code
82
- end
83
- end
84
-
85
- def generate_spec_payload
86
- to = File.join('spec/payloads', class_path, "#{file_name}.rb")
87
- template('payload.rb.erb', to)
88
- end
89
-
90
- def generate_strong_resource
91
- code = " strong_resource :#{file_name} do\n"
92
- attributes.each do |a|
93
- type = a.type
94
- type = :string if type == :text
95
- type = :number if [:float, :decimal].include?(type)
96
- code << " attribute :#{a.name}, :#{type}\n"
97
- end
98
- code << " end\n"
99
-
100
- inject_into_file 'config/initializers/strong_resources.rb', after: "StrongResources.configure do\n" do
101
- code
102
- end
103
- end
104
-
105
59
  def generate_route
106
60
  code = " resources :#{type}"
107
61
  code << ", only: [#{actions.map { |a| ":#{a}" }.join(', ')}]" if actions.length < 5
@@ -112,40 +66,11 @@ module Jsonapi
112
66
  end
113
67
 
114
68
  def generate_tests
115
- if actions?('index')
116
- to = File.join "spec/api/v1/#{file_name.pluralize}",
117
- class_path,
118
- "index_spec.rb"
119
- template('index_request_spec.rb.erb', to)
120
- end
121
-
122
- if actions?('show')
123
- to = File.join "spec/api/v1/#{file_name.pluralize}",
124
- class_path,
125
- "show_spec.rb"
126
- template('show_request_spec.rb.erb', to)
127
- end
69
+ to = File.join("spec/resources/#{file_name}", class_path, "reads_spec.rb")
70
+ template('resource_reads_spec.rb.erb', to)
128
71
 
129
- if actions?('create')
130
- to = File.join "spec/api/v1/#{file_name.pluralize}",
131
- class_path,
132
- "create_spec.rb"
133
- template('create_request_spec.rb.erb', to)
134
- end
135
-
136
- if actions?('update')
137
- to = File.join "spec/api/v1/#{file_name.pluralize}",
138
- class_path,
139
- "update_spec.rb"
140
- template('update_request_spec.rb.erb', to)
141
- end
142
-
143
- if actions?('destroy')
144
- to = File.join "spec/api/v1/#{file_name.pluralize}",
145
- class_path,
146
- "destroy_spec.rb"
147
- template('destroy_request_spec.rb.erb', to)
148
- end
72
+ to = File.join("spec/resources/#{file_name}", class_path, "writes_spec.rb")
73
+ template('resource_writes_spec.rb.erb', to)
149
74
  end
150
75
 
151
76
  def generate_resource
@@ -191,6 +116,10 @@ module Jsonapi
191
116
  class_name.safe_constantize
192
117
  end
193
118
 
119
+ def resource_klass
120
+ "#{model_klass}Resource"
121
+ end
122
+
194
123
  def type
195
124
  model_klass.name.underscore.pluralize
196
125
  end
@@ -10,5 +10,6 @@ class ApplicationResource < JsonapiCompliable::Resource
10
10
  # Subclasses can still override this default.
11
11
  # More on adapters: https://jsonapi-suite.github.io/jsonapi_compliable/JsonapiCompliable/Adapters/Abstract.html
12
12
  <%- end -%>
13
- use_adapter JsonapiCompliable::Adapters::ActiveRecord
13
+ self.abstract_class = true
14
+ self.adapter = JsonapiCompliable::Adapters::ActiveRecord::Base.new
14
15
  end
@@ -1,96 +1,51 @@
1
1
  <% module_namespacing do -%>
2
2
  class <%= model_klass.name.pluralize %>Controller < ApplicationController
3
- <%- unless omit_comments? -%>
4
- # Mark this as a JSONAPI controller, associating with the given resource
5
- <%- end -%>
6
- jsonapi resource: <%= model_klass %>Resource
7
-
8
- <%- if actions?('create', 'update') -%>
9
- <%- unless omit_comments? -%>
10
- # Reference a strong resource payload defined in
11
- # config/initializers/strong_resources.rb
12
- <%- end -%>
13
- strong_resource :<%= file_name %>
14
- <%- end -%>
15
- <%- if actions?('create', 'update') -%>
16
- <%- unless omit_comments? -%>
17
- # Run strong parameter validation for these actions.
18
- # Invalid keys will be dropped.
19
- # Invalid value types will log or raise based on the configuration
20
- # ActionController::Parameters.action_on_invalid_parameters
21
- <%- end -%>
22
- before_action :apply_strong_params, only: [:create, :update]
23
-
24
- <%- end -%>
25
3
  <%- if actions?('index') -%>
26
- <%- unless omit_comments? -%>
27
- # Start with a base scope and pass to render_jsonapi
28
- <%- end -%>
29
4
  def index
30
- <%= file_name.pluralize %> = <%= model_klass %>.all
31
- render_jsonapi(<%= file_name.pluralize %>)
5
+ <%= file_name.pluralize %> = <%= resource_klass %>.all(params)
6
+ respond_with(<%= file_name.pluralize %>)
32
7
  end
33
-
34
8
  <%- end -%>
35
9
  <%- if actions?('show') -%>
36
- <%- unless omit_comments? -%>
37
- # Call jsonapi_scope directly here so we can get behavior like
38
- # sparse fieldsets and statistics.
39
- <%- end -%>
10
+
40
11
  def show
41
- scope = jsonapi_scope(<%= model_klass %>.where(id: params[:id]))
42
- instance = scope.resolve.first
43
- raise JsonapiCompliable::Errors::RecordNotFound unless instance
44
- render_jsonapi(instance, scope: false)
12
+ <%= file_name %> = <%= resource_klass %>.find(params)
13
+ respond_with(<%= file_name.pluralize %>)
45
14
  end
46
-
47
15
  <%- end -%>
48
16
  <%- if actions?('create') -%>
49
- <%- unless omit_comments? -%>
50
- # jsonapi_create will use the configured Resource (and adapter) to persist.
51
- # This will handle nested relationships as well.
52
- # On validation errors, render correct error JSON.
53
- <%- end -%>
17
+
54
18
  def create
55
- <%= file_name %>, success = jsonapi_create.to_a
19
+ <%= file_name %> = <%= resource_klass %>.build(params)
56
20
 
57
- if success
58
- render_jsonapi(<%= file_name %>, scope: false)
21
+ if <%= file_name %>.save
22
+ respond_with(<%= file_name %>)
59
23
  else
60
- render_errors_for(<%= file_name %>)
24
+ render jsonapi_errors: <%= file_name %>
61
25
  end
62
26
  end
63
-
64
27
  <%- end -%>
65
28
  <%- if actions?('update') -%>
66
- <%- unless omit_comments? -%>
67
- # jsonapi_update will use the configured Resource (and adapter) to persist.
68
- # This will handle nested relationships as well.
69
- # On validation errors, render correct error JSON.
70
- <%- end -%>
29
+
71
30
  def update
72
- <%= file_name %>, success = jsonapi_update.to_a
31
+ <%= file_name %> = <%= resource_klass %>.find(params)
73
32
 
74
- if success
75
- render_jsonapi(<%= file_name %>, scope: false)
33
+ if <%= file_name %>.update_attributes
34
+ respond_with(<%= file_name %>)
76
35
  else
77
- render_errors_for(<%= file_name %>)
36
+ render jsonapi_errors: <%= file_name %>
78
37
  end
79
38
  end
80
-
81
39
  <%- end -%>
82
40
  <%- if actions?('destroy') -%>
83
- <%- unless omit_comments? -%>
84
- # Renders 200 OK with empty meta
85
- # http://jsonapi.org/format/#crud-deleting-responses-200
86
- <%- end -%>
41
+
87
42
  def destroy
88
- <%= file_name %>, success = jsonapi_destroy.to_a
43
+ <%= file_name %> = <%= resource_klass %>.find(params)
89
44
 
90
- if success
45
+ if <%= file_name %>.destroy
91
46
  render json: { meta: {} }
92
47
  else
93
- render_errors_for(<%= file_name %>)
48
+ render jsonapi_errors: <%= file_name %>
94
49
  end
95
50
  end
96
51
  <%- end -%>
@@ -1,53 +1,11 @@
1
- <%- unless omit_comments? -%>
2
- # Define how to query and persist a given model.
3
- # Further Resource documentation: https://jsonapi-suite.github.io/jsonapi_compliable/JsonapiCompliable/Resource.html
4
- <%- end -%>
5
1
  <% module_namespacing do -%>
6
2
  class <%= class_name %>Resource < ApplicationResource
7
- <%- unless omit_comments? -%>
8
- # Used for associating this resource with a given input.
9
- # This should match the 'type' in the corresponding serializer.
3
+ <%- attributes.each do |a| -%>
4
+ <%- if [:created_at, :updated_at].include?(a.name.to_sym) -%>
5
+ attribute :<%= a.name %>, :<%= a.type %>, writable: false
6
+ <%- else -%>
7
+ attribute :<%= a.name %>, :<%= a.type %>
10
8
  <%- end -%>
11
- type :<%= type %>
12
- <%- unless omit_comments? -%>
13
- # Associate to a Model object so we know how to persist.
14
- <%- end -%>
15
- model <%= model_klass %>
16
- <%- unless omit_comments? -%>
17
- # Customize your resource here. Some common examples:
18
- #
19
- # === Allow ?filter[name] query parameter ===
20
- # allow_filter :name
21
- #
22
- # === Enable total count, when requested ===
23
- # allow_stat total: [:count]
24
- #
25
- # === Allow sideloading/sideposting of relationships ===
26
- # belongs_to :foo,
27
- # foreign_key: :foo_id,
28
- # resource: FooResource,
29
- # scope: -> { Foo.all }
30
- #
31
- # === Custom sorting logic ===
32
- # sort do |scope, att, dir|
33
- # ... code ...
34
- # end
35
- #
36
- # === Change default sort ===
37
- # default_sort([{ title: :asc }])
38
- #
39
- # === Custom pagination logic ===
40
- # paginate do |scope, current_page, per_page|
41
- # ... code ...
42
- # end
43
- #
44
- # === Change default page size ===
45
- # default_page_size(10)
46
- #
47
- # === Change how we resolve the scope ===
48
- # def resolve(scope)
49
- # ... code ...
50
- # end
51
9
  <%- end -%>
52
10
  end
53
11
  <% end -%>
@@ -0,0 +1,62 @@
1
+ require 'rails_helper'
2
+
3
+ RSpec.describe <%= resource_klass %>, type: :resource do
4
+ describe 'serialization' do
5
+ let!(:<%= file_name %>) { create(:<%= file_name %>) }
6
+
7
+ it 'works' do
8
+ render
9
+ data = jsonapi_data[0]
10
+ expect(data.id).to eq(<%= file_name %>.id)
11
+ expect(data.jsonapi_type).to eq('<%= file_name.pluralize %>')
12
+ <%- attributes.each do |a| -%>
13
+ <%- if [:created_at, :updated_at].include?(a.name.to_sym) -%>
14
+ expect(data.<%= a.name %>).to eq(datetime(<%= file_name %>.<%= a.name %>))
15
+ <%- else -%>
16
+ expect(data.<%= a.name %>).to eq(<%= file_name %>.<%= a.name %>)
17
+ <%- end -%>
18
+ <%- end -%>
19
+ end
20
+ end
21
+ <%- if actions?('index') -%>
22
+
23
+ describe 'filtering' do
24
+ let!(:<%= file_name %>1) { create(:<%= file_name %>) }
25
+ let!(:<%= file_name %>2) { create(:<%= file_name %>) }
26
+
27
+ context 'by id' do
28
+ before do
29
+ params[:filter] = { id: { eq: <%= file_name %>2.id } }
30
+ end
31
+
32
+ it 'works' do
33
+ render
34
+ expect(d.map(&:id)).to eq([<%= file_name %>2.id])
35
+ end
36
+ end
37
+ end
38
+
39
+ describe 'sorting' do
40
+ context 'by id descending' do
41
+ let!(:<%= file_name %>1) { create(:<%= file_name %>) }
42
+ let!(:<%= file_name %>2) { create(:<%= file_name %>) }
43
+
44
+ before do
45
+ params[:sort] = '-id'
46
+ end
47
+
48
+ it 'works' do
49
+ render
50
+ expect(d.map(&:id)).to eq([
51
+ <%= file_name %>2.id,
52
+ <%= file_name %>1.id
53
+ ])
54
+ end
55
+ end
56
+ end
57
+ <%- end -%>
58
+
59
+ describe 'sideloading' do
60
+ # ... your tests ...
61
+ end
62
+ end