jsonapi_compliable 0.11.34 → 1.0.alpha.2

Sign up to get free protection for your applications and to get access to all the features.
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