jsonapi-resources 0.4.2 → 0.4.3
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 +4 -4
- data/Gemfile +2 -2
- data/README.md +103 -71
- data/Rakefile +2 -2
- data/jsonapi-resources.gemspec +2 -2
- data/lib/jsonapi-resources.rb +0 -1
- data/lib/jsonapi/active_record_operations_processor.rb +10 -2
- data/lib/jsonapi/acts_as_resource_controller.rb +26 -24
- data/lib/jsonapi/association.rb +50 -15
- data/lib/jsonapi/callbacks.rb +1 -2
- data/lib/jsonapi/configuration.rb +8 -24
- data/lib/jsonapi/error.rb +1 -2
- data/lib/jsonapi/error_codes.rb +3 -1
- data/lib/jsonapi/exceptions.rb +59 -47
- data/lib/jsonapi/include_directives.rb +11 -11
- data/lib/jsonapi/mime_types.rb +2 -2
- data/lib/jsonapi/operation.rb +28 -11
- data/lib/jsonapi/operations_processor.rb +16 -5
- data/lib/jsonapi/paginator.rb +19 -19
- data/lib/jsonapi/request.rb +175 -196
- data/lib/jsonapi/resource.rb +158 -105
- data/lib/jsonapi/resource_serializer.rb +37 -26
- data/lib/jsonapi/resources/version.rb +2 -2
- data/lib/jsonapi/response_document.rb +5 -4
- data/lib/jsonapi/routing_ext.rb +24 -19
- data/test/controllers/controller_test.rb +261 -31
- data/test/fixtures/active_record.rb +206 -8
- data/test/fixtures/book_comments.yml +2 -1
- data/test/fixtures/books.yml +1 -0
- data/test/fixtures/documents.yml +3 -0
- data/test/fixtures/people.yml +8 -1
- data/test/fixtures/pictures.yml +15 -0
- data/test/fixtures/products.yml +3 -0
- data/test/fixtures/vehicles.yml +8 -0
- data/test/helpers/{hash_helpers.rb → assertions.rb} +6 -1
- data/test/integration/requests/request_test.rb +14 -3
- data/test/integration/routes/routes_test.rb +47 -0
- data/test/test_helper.rb +27 -4
- data/test/unit/serializer/include_directives_test.rb +5 -0
- data/test/unit/serializer/polymorphic_serializer_test.rb +384 -0
- data/test/unit/serializer/serializer_test.rb +19 -1
- metadata +14 -4
@@ -13,7 +13,8 @@ ActiveRecord::Schema.define do
|
|
13
13
|
t.string :email
|
14
14
|
t.datetime :date_joined
|
15
15
|
t.belongs_to :preferences
|
16
|
-
t.integer
|
16
|
+
t.integer :hair_cut_id, index: true
|
17
|
+
t.boolean :book_admin, default: false
|
17
18
|
t.timestamps null: false
|
18
19
|
end
|
19
20
|
|
@@ -96,7 +97,7 @@ ActiveRecord::Schema.define do
|
|
96
97
|
t.string :spouse_name
|
97
98
|
t.text :bio
|
98
99
|
t.float :quality_rating
|
99
|
-
t.decimal :salary, :
|
100
|
+
t.decimal :salary, precision: 12, scale: 2
|
100
101
|
t.datetime :date_time_joined
|
101
102
|
t.date :birthday
|
102
103
|
t.time :bedtime
|
@@ -107,12 +108,14 @@ ActiveRecord::Schema.define do
|
|
107
108
|
create_table :books, force: true do |t|
|
108
109
|
t.string :title
|
109
110
|
t.string :isbn
|
111
|
+
t.boolean :banned, default: false
|
110
112
|
end
|
111
113
|
|
112
114
|
create_table :book_comments, force: true do |t|
|
113
115
|
t.text :body
|
114
116
|
t.belongs_to :book, index: true
|
115
117
|
t.integer :author_id
|
118
|
+
t.boolean :approved, default: true
|
116
119
|
t.timestamps null: false
|
117
120
|
end
|
118
121
|
|
@@ -167,6 +170,28 @@ ActiveRecord::Schema.define do
|
|
167
170
|
t.string :name
|
168
171
|
t.string :status, limit: 10
|
169
172
|
end
|
173
|
+
|
174
|
+
create_table :pictures, force: true do |t|
|
175
|
+
t.string :name
|
176
|
+
t.integer :imageable_id
|
177
|
+
t.string :imageable_type
|
178
|
+
t.timestamps null: false
|
179
|
+
end
|
180
|
+
|
181
|
+
create_table :documents, force: true do |t|
|
182
|
+
t.string :name
|
183
|
+
t.timestamps null: false
|
184
|
+
end
|
185
|
+
|
186
|
+
create_table :products, force: true do |t|
|
187
|
+
t.string :name
|
188
|
+
t.timestamps null: false
|
189
|
+
end
|
190
|
+
|
191
|
+
create_table :vehicles, force: true do |t|
|
192
|
+
t.string :type
|
193
|
+
t.integer :person_id
|
194
|
+
end
|
170
195
|
end
|
171
196
|
|
172
197
|
### MODELS
|
@@ -174,6 +199,7 @@ class Person < ActiveRecord::Base
|
|
174
199
|
has_many :posts, foreign_key: 'author_id'
|
175
200
|
has_many :comments, foreign_key: 'author_id'
|
176
201
|
has_many :expense_entries, foreign_key: 'employee_id', dependent: :restrict_with_exception
|
202
|
+
has_many :vehicles
|
177
203
|
belongs_to :preferences
|
178
204
|
belongs_to :hair_cut
|
179
205
|
|
@@ -289,6 +315,7 @@ end
|
|
289
315
|
|
290
316
|
class Book < ActiveRecord::Base
|
291
317
|
has_many :book_comments
|
318
|
+
has_many :approved_book_comments, -> { where(approved: true) }, class_name: "BookComment"
|
292
319
|
end
|
293
320
|
|
294
321
|
class BookComment < ActiveRecord::Base
|
@@ -343,6 +370,28 @@ end
|
|
343
370
|
class Category < ActiveRecord::Base
|
344
371
|
end
|
345
372
|
|
373
|
+
class Picture < ActiveRecord::Base
|
374
|
+
belongs_to :imageable, polymorphic: true
|
375
|
+
end
|
376
|
+
|
377
|
+
class Vehicle < ActiveRecord::Base
|
378
|
+
belongs_to :person
|
379
|
+
end
|
380
|
+
|
381
|
+
class Car < Vehicle
|
382
|
+
end
|
383
|
+
|
384
|
+
class Boat < Vehicle
|
385
|
+
end
|
386
|
+
|
387
|
+
class Document < ActiveRecord::Base
|
388
|
+
has_many :pictures, as: :imageable
|
389
|
+
end
|
390
|
+
|
391
|
+
class Product < ActiveRecord::Base
|
392
|
+
has_one :picture, as: :imageable
|
393
|
+
end
|
394
|
+
|
346
395
|
### PORO Data - don't do this in a production app
|
347
396
|
$breed_data = BreedData.new
|
348
397
|
$breed_data.add(Breed.new(0, 'persian'))
|
@@ -393,6 +442,18 @@ end
|
|
393
442
|
class CategoriesController < JSONAPI::ResourceController
|
394
443
|
end
|
395
444
|
|
445
|
+
class PicturesController < JSONAPI::ResourceController
|
446
|
+
end
|
447
|
+
|
448
|
+
class DocumentsController < JSONAPI::ResourceController
|
449
|
+
end
|
450
|
+
|
451
|
+
class ProductsController < JSONAPI::ResourceController
|
452
|
+
end
|
453
|
+
|
454
|
+
class ImageablesController < JSONAPI::ResourceController
|
455
|
+
end
|
456
|
+
|
396
457
|
### CONTROLLERS
|
397
458
|
module Api
|
398
459
|
module V1
|
@@ -445,9 +506,15 @@ module Api
|
|
445
506
|
end
|
446
507
|
|
447
508
|
class BooksController < JSONAPI::ResourceController
|
509
|
+
def context
|
510
|
+
{current_user: $test_user}
|
511
|
+
end
|
448
512
|
end
|
449
513
|
|
450
514
|
class BookCommentsController < JSONAPI::ResourceController
|
515
|
+
def context
|
516
|
+
{current_user: $test_user}
|
517
|
+
end
|
451
518
|
end
|
452
519
|
end
|
453
520
|
|
@@ -525,6 +592,7 @@ class PersonResource < JSONAPI::Resource
|
|
525
592
|
|
526
593
|
has_many :comments
|
527
594
|
has_many :posts
|
595
|
+
has_many :vehicles, polymorphic: true
|
528
596
|
|
529
597
|
has_one :preferences
|
530
598
|
has_one :hair_cut
|
@@ -544,6 +612,16 @@ class PersonResource < JSONAPI::Resource
|
|
544
612
|
end
|
545
613
|
end
|
546
614
|
|
615
|
+
class VehicleResource < JSONAPI::Resource
|
616
|
+
has_one :person
|
617
|
+
end
|
618
|
+
|
619
|
+
class CarResource < VehicleResource
|
620
|
+
end
|
621
|
+
|
622
|
+
class BoatResource < VehicleResource
|
623
|
+
end
|
624
|
+
|
547
625
|
class CommentResource < JSONAPI::Resource
|
548
626
|
attributes :body
|
549
627
|
has_one :post
|
@@ -611,6 +689,13 @@ class PostResource < JSONAPI::Resource
|
|
611
689
|
@model.title
|
612
690
|
end
|
613
691
|
|
692
|
+
def title=(title)
|
693
|
+
@model.title = title
|
694
|
+
if title == 'BOOM'
|
695
|
+
raise 'The Server just tested going boom. If this was a real emergency you would be really dead right now.'
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
614
699
|
filters :title, :author, :tags, :comments
|
615
700
|
filters :id, :ids
|
616
701
|
|
@@ -689,7 +774,7 @@ class BreedResource < JSONAPI::Resource
|
|
689
774
|
attribute :name, format: :title
|
690
775
|
|
691
776
|
# This is unneeded, just here for testing
|
692
|
-
routing_options :
|
777
|
+
routing_options param: :id
|
693
778
|
|
694
779
|
def self.find(filters, options = {})
|
695
780
|
breeds = []
|
@@ -764,6 +849,28 @@ class CategoryResource < JSONAPI::Resource
|
|
764
849
|
filter :status, default: 'active'
|
765
850
|
end
|
766
851
|
|
852
|
+
class PictureResource < JSONAPI::Resource
|
853
|
+
attribute :name
|
854
|
+
has_one :imageable, polymorphic: true
|
855
|
+
end
|
856
|
+
|
857
|
+
class DocumentResource < JSONAPI::Resource
|
858
|
+
attribute :name
|
859
|
+
has_many :pictures
|
860
|
+
end
|
861
|
+
|
862
|
+
class ProductResource < JSONAPI::Resource
|
863
|
+
attribute :name
|
864
|
+
has_one :picture
|
865
|
+
|
866
|
+
def picture_id
|
867
|
+
model.picture.id
|
868
|
+
end
|
869
|
+
end
|
870
|
+
|
871
|
+
class ImageableResource < JSONAPI::Resource
|
872
|
+
end
|
873
|
+
|
767
874
|
module Api
|
768
875
|
module V1
|
769
876
|
class WriterResource < JSONAPI::Resource
|
@@ -794,7 +901,6 @@ module Api
|
|
794
901
|
filters :writer
|
795
902
|
end
|
796
903
|
|
797
|
-
# AuthorResource = AuthorResource.dup
|
798
904
|
PersonResource = PersonResource.dup
|
799
905
|
CommentResource = CommentResource.dup
|
800
906
|
TagResource = TagResource.dup
|
@@ -809,27 +915,119 @@ module Api
|
|
809
915
|
EmployeeResource = EmployeeResource.dup
|
810
916
|
FriendResource = FriendResource.dup
|
811
917
|
HairCutResource = HairCutResource.dup
|
918
|
+
VehicleResource = VehicleResource.dup
|
919
|
+
CarResource = CarResource.dup
|
920
|
+
BoatResource = BoatResource.dup
|
812
921
|
end
|
813
922
|
end
|
814
923
|
|
815
924
|
module Api
|
816
925
|
module V2
|
817
926
|
PreferencesResource = PreferencesResource.dup
|
818
|
-
# AuthorResource = AuthorResource.dup
|
819
927
|
PersonResource = PersonResource.dup
|
820
928
|
PostResource = PostResource.dup
|
821
929
|
|
822
930
|
class BookResource < JSONAPI::Resource
|
823
931
|
attribute :title
|
824
|
-
|
932
|
+
attributes :isbn, :banned
|
933
|
+
|
934
|
+
has_many :book_comments, relation_name: -> (options = {}) {
|
935
|
+
context = options[:context]
|
936
|
+
current_user = context ? context[:current_user] : nil
|
825
937
|
|
826
|
-
|
938
|
+
unless current_user && current_user.book_admin
|
939
|
+
:approved_book_comments
|
940
|
+
else
|
941
|
+
:book_comments
|
942
|
+
end
|
943
|
+
}
|
944
|
+
|
945
|
+
filters :banned, :book_comments
|
946
|
+
|
947
|
+
class << self
|
948
|
+
def apply_filter(records, filter, value, options)
|
949
|
+
context = options[:context]
|
950
|
+
current_user = context ? context[:current_user] : nil
|
951
|
+
|
952
|
+
case filter
|
953
|
+
when :banned
|
954
|
+
# Only book admins my filter for banned books
|
955
|
+
if current_user && current_user.book_admin
|
956
|
+
return records.where('books.banned = ?', value[0] == 'true')
|
957
|
+
end
|
958
|
+
else
|
959
|
+
return super(records, filter, value)
|
960
|
+
end
|
961
|
+
end
|
962
|
+
|
963
|
+
def books
|
964
|
+
Book.arel_table
|
965
|
+
end
|
966
|
+
|
967
|
+
def not_banned_books
|
968
|
+
books[:banned].eq(false)
|
969
|
+
end
|
970
|
+
|
971
|
+
def records(options = {})
|
972
|
+
context = options[:context]
|
973
|
+
current_user = context ? context[:current_user] : nil
|
974
|
+
|
975
|
+
records = _model_class
|
976
|
+
# Hide the banned books from people who are not book admins
|
977
|
+
unless current_user && current_user.book_admin
|
978
|
+
records = records.where(not_banned_books)
|
979
|
+
end
|
980
|
+
records
|
981
|
+
end
|
982
|
+
end
|
827
983
|
end
|
828
984
|
|
829
985
|
class BookCommentResource < JSONAPI::Resource
|
830
|
-
attributes :body
|
986
|
+
attributes :body, :approved
|
987
|
+
|
831
988
|
has_one :book
|
832
989
|
has_one :author, class_name: 'Person'
|
990
|
+
|
991
|
+
filters :approved, :book
|
992
|
+
|
993
|
+
class << self
|
994
|
+
def book_comments
|
995
|
+
BookComment.arel_table
|
996
|
+
end
|
997
|
+
|
998
|
+
def approved_comments(approved = true)
|
999
|
+
book_comments[:approved].eq(approved)
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
def apply_filter(records, filter, value, options)
|
1003
|
+
context = options[:context]
|
1004
|
+
current_user = context ? context[:current_user] : nil
|
1005
|
+
|
1006
|
+
case filter
|
1007
|
+
when :approved
|
1008
|
+
# Only book admins my filter for unapproved comments
|
1009
|
+
if current_user && current_user.book_admin
|
1010
|
+
records.where(approved_comments(value[0] == 'true'))
|
1011
|
+
end
|
1012
|
+
else
|
1013
|
+
#:nocov:
|
1014
|
+
return super(records, filter, value)
|
1015
|
+
#:nocov:
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
def records(options = {})
|
1020
|
+
context = options[:context]
|
1021
|
+
current_user = context ? context[:current_user] : nil
|
1022
|
+
|
1023
|
+
records = _model_class
|
1024
|
+
# Hide the unapproved comments from people who are not book admins
|
1025
|
+
unless current_user && current_user.book_admin
|
1026
|
+
records = records.where(approved_comments)
|
1027
|
+
end
|
1028
|
+
records
|
1029
|
+
end
|
1030
|
+
end
|
833
1031
|
end
|
834
1032
|
end
|
835
1033
|
end
|
@@ -4,8 +4,9 @@
|
|
4
4
|
book_<%= book_num %>_comment_<%= comment_num %>:
|
5
5
|
id: <%= comment_id %>
|
6
6
|
body: This is comment <%= comment_num %> on book <%= book_num %>.
|
7
|
-
author_id:
|
7
|
+
author_id: <%= book_num.even? ? comment_id % 2 : (comment_id % 2) + 2 %>
|
8
8
|
book_id: <%= book_num %>
|
9
|
+
approved: <%= comment_num.even? %>
|
9
10
|
<% comment_id = comment_id + 1 %>
|
10
11
|
<% end %>
|
11
12
|
<% end %>
|
data/test/fixtures/books.yml
CHANGED
data/test/fixtures/people.yml
CHANGED
@@ -21,4 +21,11 @@ d:
|
|
21
21
|
id: 4
|
22
22
|
name: Tag Crazy Author
|
23
23
|
email: taggy@xyz.fake
|
24
|
-
date_joined: <%= DateTime.parse('2013-11-30 4:20:00 UTC +00:00') %>
|
24
|
+
date_joined: <%= DateTime.parse('2013-11-30 4:20:00 UTC +00:00') %>
|
25
|
+
|
26
|
+
e:
|
27
|
+
id: 5
|
28
|
+
name: Wilma Librarian
|
29
|
+
email: lib@xyz.fake
|
30
|
+
date_joined: <%= DateTime.parse('2013-11-30 4:20:00 UTC +00:00') %>
|
31
|
+
book_admin: true
|
@@ -1,8 +1,13 @@
|
|
1
1
|
module Helpers
|
2
|
-
module
|
2
|
+
module Assertions
|
3
3
|
def assert_hash_equals(exp, act, msg = nil)
|
4
4
|
msg = message(msg, '') { diff exp, act }
|
5
5
|
assert(matches_hash?(exp, act, {exact: true}), msg)
|
6
6
|
end
|
7
|
+
|
8
|
+
def assert_array_equals(exp, act, msg = nil)
|
9
|
+
msg = message(msg, '') { diff exp, act }
|
10
|
+
assert(matches_array?(exp, act, {exact: true}), msg)
|
11
|
+
end
|
7
12
|
end
|
8
13
|
end
|
@@ -4,6 +4,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
4
4
|
def setup
|
5
5
|
JSONAPI.configuration.json_key_format = :underscored_key
|
6
6
|
JSONAPI.configuration.route_format = :underscored_route
|
7
|
+
$test_user = Person.find(1)
|
7
8
|
end
|
8
9
|
|
9
10
|
def after_teardown
|
@@ -337,7 +338,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
337
338
|
Api::V2::BookResource.paginator :none
|
338
339
|
get '/api/v2/books'
|
339
340
|
assert_equal 200, status
|
340
|
-
assert_equal
|
341
|
+
assert_equal 901, json_response['data'].size
|
341
342
|
end
|
342
343
|
|
343
344
|
def test_pagination_offset_style
|
@@ -385,7 +386,7 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
385
386
|
get '/api/v2/books/1/book_comments?page[limit]=10'
|
386
387
|
assert_equal 200, status
|
387
388
|
assert_equal 10, json_response['data'].size
|
388
|
-
assert_equal 'This is comment
|
389
|
+
assert_equal 'This is comment 18 on book 1.', json_response['data'][9]['attributes']['body']
|
389
390
|
end
|
390
391
|
|
391
392
|
def test_pagination_related_resources_data_includes
|
@@ -394,9 +395,19 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
394
395
|
get '/api/v2/books/1/book_comments?page[limit]=10&include=author,book'
|
395
396
|
assert_equal 200, status
|
396
397
|
assert_equal 10, json_response['data'].size
|
397
|
-
assert_equal 'This is comment
|
398
|
+
assert_equal 'This is comment 18 on book 1.', json_response['data'][9]['attributes']['body']
|
398
399
|
end
|
399
400
|
|
401
|
+
# def test_pagination_related_resources_data_includes
|
402
|
+
# Api::V2::BookResource.paginator :none
|
403
|
+
# Api::V2::BookCommentResource.paginator :none
|
404
|
+
# get '/api/v2/books?filter[]'
|
405
|
+
# assert_equal 200, status
|
406
|
+
# assert_equal 10, json_response['data'].size
|
407
|
+
# assert_equal 'This is comment 18 on book 1.', json_response['data'][9]['attributes']['body']
|
408
|
+
# end
|
409
|
+
|
410
|
+
|
400
411
|
def test_flow_self
|
401
412
|
get '/posts'
|
402
413
|
assert_equal 200, status
|