jsonapi-resources 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +56 -12
- data/lib/jsonapi/exceptions.rb +1 -1
- data/lib/jsonapi/paginator.rb +1 -1
- data/lib/jsonapi/resource.rb +49 -43
- data/lib/jsonapi/resource_serializer.rb +1 -1
- data/lib/jsonapi/resources/version.rb +1 -1
- data/lib/jsonapi/routing_ext.rb +28 -18
- data/test/controllers/controller_test.rb +72 -4
- data/test/fixtures/active_record.rb +52 -27
- data/test/fixtures/makes.yml +2 -0
- data/test/fixtures/vehicles.yml +3 -2
- data/test/fixtures/web_pages.yml +3 -0
- data/test/integration/requests/request_test.rb +13 -0
- data/test/lib/generators/jsonapi/resource_generator_test.rb +1 -1
- data/test/test_helper.rb +4 -3
- data/test/unit/jsonapi_request/jsonapi_request_test.rb +0 -1
- data/test/unit/pagination/paged_paginator_test.rb +6 -6
- data/test/unit/resource/resource_test.rb +93 -8
- data/test/unit/serializer/polymorphic_serializer_test.rb +2 -2
- data/test/unit/serializer/serializer_test.rb +135 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 885923e290e5ac1db4d49ed616f8f93b978edabe
|
4
|
+
data.tar.gz: f9ba1571591967f00bd09b274b2aae34b01153e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 305b0ae0b9f37ec829b178769044957f12f90c0272b69339b930deabac8f8f34bf310539597eeb01adc911c3bb897fd9aa60b52b3b077c9d925e926fa9fca157
|
7
|
+
data.tar.gz: d9716961f3daeb72d56ae12b37c42960f8dcd99042f8cf6d884d67081ceebfc1ae20510eef1d682fe504e726433ce443dc5007c6cd3da0a1065c1eef708948c3
|
data/README.md
CHANGED
@@ -106,6 +106,50 @@ class ContactResource < BaseResource
|
|
106
106
|
end
|
107
107
|
```
|
108
108
|
|
109
|
+
##### Immutable Resources
|
110
|
+
|
111
|
+
Resources that are immutable should be declared as such with the `immutable` method. Immutable resources will only
|
112
|
+
generate routes for `index`, `show` and `show_relationship`.
|
113
|
+
|
114
|
+
###### Immutable for Readonly
|
115
|
+
|
116
|
+
Some resources are read-only and are not to be modified through the API. Declaring a resource as immutable prevents
|
117
|
+
creation of routes that allow modification of the resource.
|
118
|
+
|
119
|
+
###### Immutable Heterogeneous Collections
|
120
|
+
|
121
|
+
Immutable resources can be used as the basis for a heterogeneous collection. Resources in heterogeneous collections can
|
122
|
+
still be mutated through their own type-specific endpoints.
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
class VehicleResource < JSONAPI::Resource
|
126
|
+
immutable
|
127
|
+
|
128
|
+
has_one :owner
|
129
|
+
attributes :make, :model, :serial_number
|
130
|
+
end
|
131
|
+
|
132
|
+
class CarResource < VehicleResource
|
133
|
+
attributes :drive_layout
|
134
|
+
has_one :driver
|
135
|
+
end
|
136
|
+
|
137
|
+
class BoatResource < VehicleResource
|
138
|
+
attributes :length_at_water_line
|
139
|
+
has_one :captain
|
140
|
+
end
|
141
|
+
|
142
|
+
# routes
|
143
|
+
jsonapi_resources :vehicles
|
144
|
+
jsonapi_resources :cars
|
145
|
+
jsonapi_resources :boats
|
146
|
+
|
147
|
+
```
|
148
|
+
|
149
|
+
In the above example vehicles are immutable. A call to `/vehicles` or `/vehicles/1` will return vehicles with types
|
150
|
+
of either `car` or `boat`. But calls to PUT or POST a `car` must be made to `/cars`. The rails models backing the above
|
151
|
+
code use Single Table Inheritance.
|
152
|
+
|
109
153
|
#### Attributes
|
110
154
|
|
111
155
|
Any of a resource's attributes that are accessible must be explicitly declared. Single attributes can be declared using
|
@@ -516,7 +560,7 @@ section for additional details on raising errors.
|
|
516
560
|
class BaseResource < JSONAPI::Resource
|
517
561
|
def records_for(relation_name)
|
518
562
|
context = options[:context]
|
519
|
-
records =
|
563
|
+
records = _model.public_send(relation_name)
|
520
564
|
|
521
565
|
unless context[:current_user].can_view?(records)
|
522
566
|
raise NotAuthorizedError
|
@@ -779,12 +823,12 @@ Callbacks can be defined for the following `JSONAPI::Resource` events:
|
|
779
823
|
- `:update`
|
780
824
|
- `:remove`
|
781
825
|
- `:save`
|
782
|
-
- `:
|
783
|
-
- `:
|
784
|
-
- `:
|
785
|
-
- `:
|
786
|
-
- `:
|
787
|
-
- `:
|
826
|
+
- `:create_to_many_link`
|
827
|
+
- `:replace_to_many_links`
|
828
|
+
- `:create_to_one_link`
|
829
|
+
- `:replace_to_one_link`
|
830
|
+
- `:remove_to_many_link`
|
831
|
+
- `:remove_to_one_link`
|
788
832
|
- `:replace_fields`
|
789
833
|
|
790
834
|
##### `JSONAPI::OperationsProcessor` Callbacks
|
@@ -800,11 +844,11 @@ Callbacks can also be defined for `JSONAPI::OperationsProcessor` events:
|
|
800
844
|
- `:create_resource_operation`: A `create_resource_operation`.
|
801
845
|
- `:remove_resource_operation`: A `remove_resource_operation`.
|
802
846
|
- `:replace_fields_operation`: A `replace_fields_operation`.
|
803
|
-
- `:
|
804
|
-
- `:
|
805
|
-
- `:
|
806
|
-
- `:
|
807
|
-
- `:
|
847
|
+
- `:replace_to_one_relationship_operation`: A `replace_to_one_relationship_operation`.
|
848
|
+
- `:create_to_many_relationship_operation`: A `create_to_many_relationship_operation`.
|
849
|
+
- `:replace_to_many_relationship_operation`: A `replace_to_many_relationship_operation`.
|
850
|
+
- `:remove_to_many_relationship_operation`: A `remove_to_many_relationship_operation`.
|
851
|
+
- `:remove_to_one_relationship_operation`: A `remove_to_one_relationship_operation`.
|
808
852
|
|
809
853
|
The operation callbacks have access to two meta data hashes, `@operations_meta` and `@operation_meta`, two links hashes,
|
810
854
|
`@operations_links` and `@operation_links`, the full list of `@operations`, each individual `@operation` and the
|
data/lib/jsonapi/exceptions.rb
CHANGED
@@ -297,7 +297,7 @@ module JSONAPI
|
|
297
297
|
attr_reader :error_messages, :resource_relationships
|
298
298
|
|
299
299
|
def initialize(resource)
|
300
|
-
@error_messages = resource.
|
300
|
+
@error_messages = resource._model.errors.messages
|
301
301
|
@resource_relationships = resource.class._relationships.keys
|
302
302
|
@key_formatter = JSONAPI.configuration.key_formatter
|
303
303
|
end
|
data/lib/jsonapi/paginator.rb
CHANGED
data/lib/jsonapi/resource.rb
CHANGED
@@ -7,7 +7,6 @@ module JSONAPI
|
|
7
7
|
@@resource_types = {}
|
8
8
|
|
9
9
|
attr_reader :context
|
10
|
-
attr_reader :model
|
11
10
|
|
12
11
|
define_jsonapi_resources_callbacks :create,
|
13
12
|
:update,
|
@@ -27,8 +26,12 @@ module JSONAPI
|
|
27
26
|
@context = context
|
28
27
|
end
|
29
28
|
|
29
|
+
def _model
|
30
|
+
@model
|
31
|
+
end
|
32
|
+
|
30
33
|
def id
|
31
|
-
|
34
|
+
_model.public_send(self.class._primary_key)
|
32
35
|
end
|
33
36
|
|
34
37
|
def is_new?
|
@@ -112,7 +115,7 @@ module JSONAPI
|
|
112
115
|
# Override this on a resource to customize how the associated records
|
113
116
|
# are fetched for a model. Particularly helpful for authorization.
|
114
117
|
def records_for(relation_name)
|
115
|
-
|
118
|
+
_model.public_send relation_name
|
116
119
|
end
|
117
120
|
|
118
121
|
private
|
@@ -169,7 +172,7 @@ module JSONAPI
|
|
169
172
|
# TODO: Add option to skip relations that already exist instead of returning an error?
|
170
173
|
relation = @model.public_send(relation_name).where(relationship.primary_key => relationship_key_value).first
|
171
174
|
if relation.nil?
|
172
|
-
@model.public_send(relation_name) << related_resource.
|
175
|
+
@model.public_send(relation_name) << related_resource._model
|
173
176
|
else
|
174
177
|
fail JSONAPI::Exceptions::HasManyRelationExists.new(relationship_key_value)
|
175
178
|
end
|
@@ -198,8 +201,8 @@ module JSONAPI
|
|
198
201
|
def _replace_polymorphic_to_one_link(relationship_type, key_value, key_type)
|
199
202
|
relationship = self.class._relationships[relationship_type.to_sym]
|
200
203
|
|
201
|
-
|
202
|
-
|
204
|
+
_model.public_send("#{relationship.foreign_key}=", key_value)
|
205
|
+
_model.public_send("#{relationship.polymorphic_type}=", key_type.to_s.classify)
|
203
206
|
|
204
207
|
@save_needed = true
|
205
208
|
|
@@ -258,6 +261,7 @@ module JSONAPI
|
|
258
261
|
class << self
|
259
262
|
def inherited(base)
|
260
263
|
base.abstract(false)
|
264
|
+
base.immutable(false)
|
261
265
|
base._attributes = (_attributes || {}).dup
|
262
266
|
base._relationships = (_relationships || {}).dup
|
263
267
|
base._allowed_filters = (_allowed_filters || Set.new).dup
|
@@ -499,7 +503,7 @@ module JSONAPI
|
|
499
503
|
|
500
504
|
resources = []
|
501
505
|
records.each do |model|
|
502
|
-
resources.push new(model, context)
|
506
|
+
resources.push resource_for(resource_type_for(model)).new(model, context)
|
503
507
|
end
|
504
508
|
|
505
509
|
resources
|
@@ -511,7 +515,11 @@ module JSONAPI
|
|
511
515
|
records = apply_includes(records, options)
|
512
516
|
model = records.where({_primary_key => key}).first
|
513
517
|
fail JSONAPI::Exceptions::RecordNotFound.new(key) if model.nil?
|
514
|
-
new(model, context)
|
518
|
+
resource_for(resource_type_for(model)).new(model, context)
|
519
|
+
end
|
520
|
+
|
521
|
+
def resource_type_for(model)
|
522
|
+
self.module_path + model.class.to_s.underscore
|
515
523
|
end
|
516
524
|
|
517
525
|
# Override this method if you want to customize the relation for
|
@@ -554,40 +562,28 @@ module JSONAPI
|
|
554
562
|
|
555
563
|
def verify_key(key, context = nil)
|
556
564
|
key_type = resource_key_type
|
557
|
-
verification_proc = case key_type
|
558
565
|
|
566
|
+
case key_type
|
559
567
|
when :integer
|
560
|
-
|
561
|
-
|
562
|
-
return key if key.nil?
|
563
|
-
Integer(key)
|
564
|
-
rescue
|
565
|
-
raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
|
566
|
-
end
|
567
|
-
}
|
568
|
+
return if key.nil?
|
569
|
+
Integer(key)
|
568
570
|
when :string
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
end
|
576
|
-
}
|
571
|
+
return if key.nil?
|
572
|
+
if key.to_s.include?(',')
|
573
|
+
raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
|
574
|
+
else
|
575
|
+
key
|
576
|
+
end
|
577
577
|
when :uuid
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
end
|
585
|
-
}
|
578
|
+
return if key.nil?
|
579
|
+
if key.to_s.match(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/)
|
580
|
+
key
|
581
|
+
else
|
582
|
+
raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
|
583
|
+
end
|
586
584
|
else
|
587
|
-
key_type
|
585
|
+
key_type.call(key, context)
|
588
586
|
end
|
589
|
-
|
590
|
-
verification_proc.call(key, context)
|
591
587
|
rescue
|
592
588
|
raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key)
|
593
589
|
end
|
@@ -664,6 +660,18 @@ module JSONAPI
|
|
664
660
|
@abstract
|
665
661
|
end
|
666
662
|
|
663
|
+
def immutable(val = true)
|
664
|
+
@immutable = val
|
665
|
+
end
|
666
|
+
|
667
|
+
def _immutable
|
668
|
+
@immutable
|
669
|
+
end
|
670
|
+
|
671
|
+
def mutable?
|
672
|
+
!@immutable
|
673
|
+
end
|
674
|
+
|
667
675
|
def _model_class
|
668
676
|
return nil if _abstract
|
669
677
|
|
@@ -678,7 +686,7 @@ module JSONAPI
|
|
678
686
|
end
|
679
687
|
|
680
688
|
def module_path
|
681
|
-
|
689
|
+
name =~ /::[^:]+\Z/ ? ($`.freeze.gsub('::', '/') + '/').underscore : ''
|
682
690
|
end
|
683
691
|
|
684
692
|
def construct_order_options(sort_params)
|
@@ -702,13 +710,13 @@ module JSONAPI
|
|
702
710
|
def check_reserved_attribute_name(name)
|
703
711
|
# Allow :id since it can be used to specify the format. Since it is a method on the base Resource
|
704
712
|
# an attribute method won't be created for it.
|
705
|
-
if [:type
|
713
|
+
if [:type].include?(name.to_sym)
|
706
714
|
warn "[NAME COLLISION] `#{name}` is a reserved key in #{@@resource_types[_type]}."
|
707
715
|
end
|
708
716
|
end
|
709
717
|
|
710
718
|
def check_reserved_relationship_name(name)
|
711
|
-
if [:id, :ids, :type, :types
|
719
|
+
if [:id, :ids, :type, :types].include?(name.to_sym)
|
712
720
|
warn "[NAME COLLISION] `#{name}` is a reserved relationship name in #{@@resource_types[_type]}."
|
713
721
|
end
|
714
722
|
end
|
@@ -755,7 +763,7 @@ module JSONAPI
|
|
755
763
|
define_method attr do |options = {}|
|
756
764
|
if relationship.polymorphic?
|
757
765
|
associated_model = public_send(associated_records_method_name)
|
758
|
-
resource_klass = Resource.resource_for(self.class.
|
766
|
+
resource_klass = Resource.resource_for(self.class.resource_type_for(associated_model)) if associated_model
|
759
767
|
return resource_klass.new(associated_model, @context) if resource_klass
|
760
768
|
else
|
761
769
|
resource_klass = relationship.resource_klass
|
@@ -808,9 +816,7 @@ module JSONAPI
|
|
808
816
|
end
|
809
817
|
|
810
818
|
return records.collect do |record|
|
811
|
-
|
812
|
-
resource_klass = Resource.resource_for(self.class.module_path + record.class.to_s.underscore)
|
813
|
-
end
|
819
|
+
resource_klass = Resource.resource_for(self.class.resource_type_for(record))
|
814
820
|
resource_klass.new(record, @context)
|
815
821
|
end
|
816
822
|
end unless method_defined?(attr)
|
@@ -282,7 +282,7 @@ module JSONAPI
|
|
282
282
|
def foreign_key_types_and_values(source, relationship)
|
283
283
|
if relationship.is_a?(JSONAPI::Relationship::ToMany)
|
284
284
|
if relationship.polymorphic?
|
285
|
-
source.
|
285
|
+
source._model.public_send(relationship.name).pluck(:type, :id).map do |type, id|
|
286
286
|
[type.pluralize, IdValueFormatter.format(id)]
|
287
287
|
end
|
288
288
|
else
|
data/lib/jsonapi/routing_ext.rb
CHANGED
@@ -80,6 +80,12 @@ module ActionDispatch
|
|
80
80
|
options[:except] = [:new, :edit]
|
81
81
|
end
|
82
82
|
|
83
|
+
if res._immutable
|
84
|
+
options[:except] << :create
|
85
|
+
options[:except] << :update
|
86
|
+
options[:except] << :destroy
|
87
|
+
end
|
88
|
+
|
83
89
|
resources @resource_type, options do
|
84
90
|
@scope[:jsonapi_resource] = @resource_type
|
85
91
|
|
@@ -117,14 +123,16 @@ module ActionDispatch
|
|
117
123
|
action: 'show_relationship', relationship: link_type.to_s, via: [:get]
|
118
124
|
end
|
119
125
|
|
120
|
-
if
|
121
|
-
|
122
|
-
|
123
|
-
|
126
|
+
if res.mutable?
|
127
|
+
if methods.include?(:update)
|
128
|
+
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
129
|
+
action: 'update_relationship', relationship: link_type.to_s, via: [:put, :patch]
|
130
|
+
end
|
124
131
|
|
125
|
-
|
126
|
-
|
127
|
-
|
132
|
+
if methods.include?(:destroy)
|
133
|
+
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
134
|
+
action: 'destroy_relationship', relationship: link_type.to_s, via: [:delete]
|
135
|
+
end
|
128
136
|
end
|
129
137
|
end
|
130
138
|
|
@@ -143,19 +151,21 @@ module ActionDispatch
|
|
143
151
|
action: 'show_relationship', relationship: link_type.to_s, via: [:get]
|
144
152
|
end
|
145
153
|
|
146
|
-
if
|
147
|
-
|
148
|
-
|
149
|
-
|
154
|
+
if res.mutable?
|
155
|
+
if methods.include?(:create)
|
156
|
+
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
157
|
+
action: 'create_relationship', relationship: link_type.to_s, via: [:post]
|
158
|
+
end
|
150
159
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
160
|
+
if methods.include?(:update)
|
161
|
+
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
162
|
+
action: 'update_relationship', relationship: link_type.to_s, via: [:put, :patch]
|
163
|
+
end
|
155
164
|
|
156
|
-
|
157
|
-
|
158
|
-
|
165
|
+
if methods.include?(:destroy)
|
166
|
+
match "relationships/#{formatted_relationship_name}", controller: options[:controller],
|
167
|
+
action: 'destroy_relationship', relationship: link_type.to_s, via: [:delete]
|
168
|
+
end
|
159
169
|
end
|
160
170
|
end
|
161
171
|
|
@@ -3069,15 +3069,15 @@ class Api::V1::CratersControllerTest < ActionController::TestCase
|
|
3069
3069
|
data: [
|
3070
3070
|
{id:"A4D3",
|
3071
3071
|
type:"craters",
|
3072
|
-
links:{self: "http://test.host/craters/A4D3"},
|
3072
|
+
links:{self: "http://test.host/api/v1/craters/A4D3"},
|
3073
3073
|
attributes:{code: "A4D3", description: "Small crater"},
|
3074
|
-
relationships:{moon: {links: {self: "http://test.host/craters/A4D3/relationships/moon", related: "http://test.host/craters/A4D3/moon"}}}
|
3074
|
+
relationships:{moon: {links: {self: "http://test.host/api/v1/craters/A4D3/relationships/moon", related: "http://test.host/api/v1/craters/A4D3/moon"}}}
|
3075
3075
|
},
|
3076
3076
|
{id: "S56D",
|
3077
3077
|
type: "craters",
|
3078
|
-
links:{self: "http://test.host/craters/S56D"},
|
3078
|
+
links:{self: "http://test.host/api/v1/craters/S56D"},
|
3079
3079
|
attributes:{code: "S56D", description: "Very large crater"},
|
3080
|
-
relationships:{moon: {links: {self: "http://test.host/craters/S56D/relationships/moon", related: "http://test.host/craters/S56D/moon"}}}
|
3080
|
+
relationships:{moon: {links: {self: "http://test.host/api/v1/craters/S56D/relationships/moon", related: "http://test.host/api/v1/craters/S56D/moon"}}}
|
3081
3081
|
}
|
3082
3082
|
]
|
3083
3083
|
}
|
@@ -3091,3 +3091,71 @@ class Api::V1::CratersControllerTest < ActionController::TestCase
|
|
3091
3091
|
assert_equal "1", json_response['data']['id']
|
3092
3092
|
end
|
3093
3093
|
end
|
3094
|
+
|
3095
|
+
class CarsControllerTest < ActionController::TestCase
|
3096
|
+
def setup
|
3097
|
+
JSONAPI.configuration.json_key_format = :camelized_key
|
3098
|
+
end
|
3099
|
+
|
3100
|
+
def test_create_sti
|
3101
|
+
set_content_type_header!
|
3102
|
+
post :create,
|
3103
|
+
{
|
3104
|
+
data: {
|
3105
|
+
type: 'cars',
|
3106
|
+
attributes: {
|
3107
|
+
make: 'Toyota',
|
3108
|
+
model: 'Tercel',
|
3109
|
+
serialNumber: 'asasdsdadsa13544235',
|
3110
|
+
driveLayout: 'FWD'
|
3111
|
+
}
|
3112
|
+
}
|
3113
|
+
}
|
3114
|
+
|
3115
|
+
assert_response :created
|
3116
|
+
assert json_response['data'].is_a?(Hash)
|
3117
|
+
assert_equal 'cars', json_response['data']['type']
|
3118
|
+
assert_equal 'Toyota', json_response['data']['attributes']['make']
|
3119
|
+
assert_equal 'FWD', json_response['data']['attributes']['driveLayout']
|
3120
|
+
end
|
3121
|
+
end
|
3122
|
+
|
3123
|
+
class VehiclesControllerTest < ActionController::TestCase
|
3124
|
+
def setup
|
3125
|
+
JSONAPI.configuration.json_key_format = :camelized_key
|
3126
|
+
end
|
3127
|
+
|
3128
|
+
def test_immutable_create_not_supported
|
3129
|
+
set_content_type_header!
|
3130
|
+
|
3131
|
+
assert_raises ActionController::UrlGenerationError do
|
3132
|
+
post :create,
|
3133
|
+
{
|
3134
|
+
data: {
|
3135
|
+
type: 'cars',
|
3136
|
+
attributes: {
|
3137
|
+
make: 'Toyota',
|
3138
|
+
model: 'Corrola',
|
3139
|
+
serialNumber: 'dsvffsfv',
|
3140
|
+
driveLayout: 'FWD'
|
3141
|
+
}
|
3142
|
+
}
|
3143
|
+
}
|
3144
|
+
end
|
3145
|
+
end
|
3146
|
+
|
3147
|
+
def test_immutable_update_not_supported
|
3148
|
+
set_content_type_header!
|
3149
|
+
|
3150
|
+
assert_raises ActionController::UrlGenerationError do
|
3151
|
+
patch :update,
|
3152
|
+
data: {
|
3153
|
+
id: '1',
|
3154
|
+
type: 'cars',
|
3155
|
+
attributes: {
|
3156
|
+
make: 'Toyota',
|
3157
|
+
}
|
3158
|
+
}
|
3159
|
+
end
|
3160
|
+
end
|
3161
|
+
end
|
@@ -214,12 +214,34 @@ ActiveRecord::Schema.define do
|
|
214
214
|
create_table :vehicles, force: true do |t|
|
215
215
|
t.string :type
|
216
216
|
t.string :make
|
217
|
-
t.string :
|
217
|
+
t.string :model
|
218
218
|
t.string :length_at_water_line
|
219
219
|
t.string :drive_layout
|
220
220
|
t.string :serial_number
|
221
221
|
t.integer :person_id
|
222
222
|
end
|
223
|
+
|
224
|
+
create_table :makes, force: true do |t|
|
225
|
+
t.string :model
|
226
|
+
end
|
227
|
+
|
228
|
+
# special cases - fields that look like they should be reserved names
|
229
|
+
create_table :hrefs, force: true do |t|
|
230
|
+
t.string :name
|
231
|
+
end
|
232
|
+
|
233
|
+
create_table :links, force: true do |t|
|
234
|
+
t.string :name
|
235
|
+
end
|
236
|
+
|
237
|
+
create_table :web_pages, force: true do |t|
|
238
|
+
t.string :href
|
239
|
+
t.string :link
|
240
|
+
end
|
241
|
+
|
242
|
+
create_table :questionables, force: true do |t|
|
243
|
+
end
|
244
|
+
# special cases
|
223
245
|
end
|
224
246
|
|
225
247
|
### MODELS
|
@@ -474,6 +496,12 @@ class Product < ActiveRecord::Base
|
|
474
496
|
has_one :picture, as: :imageable
|
475
497
|
end
|
476
498
|
|
499
|
+
class Make < ActiveRecord::Base
|
500
|
+
end
|
501
|
+
|
502
|
+
class WebPage < ActiveRecord::Base
|
503
|
+
end
|
504
|
+
|
477
505
|
### OperationsProcessor
|
478
506
|
class CountingActiveRecordOperationsProcessor < ActiveRecordOperationsProcessor
|
479
507
|
after_find_operation do
|
@@ -558,6 +586,15 @@ end
|
|
558
586
|
class ImageablesController < JSONAPI::ResourceController
|
559
587
|
end
|
560
588
|
|
589
|
+
class VehiclesController < JSONAPI::ResourceController
|
590
|
+
end
|
591
|
+
|
592
|
+
class CarsController < JSONAPI::ResourceController
|
593
|
+
end
|
594
|
+
|
595
|
+
class BoatsController < JSONAPI::ResourceController
|
596
|
+
end
|
597
|
+
|
561
598
|
### CONTROLLERS
|
562
599
|
module Api
|
563
600
|
module V1
|
@@ -701,7 +738,7 @@ class BaseResource < JSONAPI::Resource
|
|
701
738
|
end
|
702
739
|
|
703
740
|
class PersonResource < BaseResource
|
704
|
-
attributes :
|
741
|
+
attributes :name, :email
|
705
742
|
attribute :date_joined, format: :date_with_timezone
|
706
743
|
|
707
744
|
has_many :comments
|
@@ -727,8 +764,10 @@ class PersonResource < BaseResource
|
|
727
764
|
end
|
728
765
|
|
729
766
|
class VehicleResource < JSONAPI::Resource
|
767
|
+
immutable
|
768
|
+
|
730
769
|
has_one :person
|
731
|
-
attributes :make, :
|
770
|
+
attributes :make, :model, :serial_number
|
732
771
|
end
|
733
772
|
|
734
773
|
class CarResource < VehicleResource
|
@@ -763,12 +802,6 @@ class TagResource < JSONAPI::Resource
|
|
763
802
|
#has_many :planets
|
764
803
|
end
|
765
804
|
|
766
|
-
class SpecialTagResource < JSONAPI::Resource
|
767
|
-
attributes :name
|
768
|
-
|
769
|
-
has_many :posts
|
770
|
-
end
|
771
|
-
|
772
805
|
class SectionResource < JSONAPI::Resource
|
773
806
|
attributes 'name'
|
774
807
|
end
|
@@ -893,7 +926,6 @@ class FriendResource < JSONAPI::Resource
|
|
893
926
|
end
|
894
927
|
|
895
928
|
class BreedResource < JSONAPI::Resource
|
896
|
-
attribute :id, format_misspelled: :does_not_exist
|
897
929
|
attribute :name, format: :title
|
898
930
|
|
899
931
|
# This is unneeded, just here for testing
|
@@ -1003,13 +1035,22 @@ class ProductResource < JSONAPI::Resource
|
|
1003
1035
|
has_one :picture, always_include_linkage_data: true
|
1004
1036
|
|
1005
1037
|
def picture_id
|
1006
|
-
|
1038
|
+
_model.picture.id
|
1007
1039
|
end
|
1008
1040
|
end
|
1009
1041
|
|
1010
1042
|
class ImageableResource < JSONAPI::Resource
|
1011
1043
|
end
|
1012
1044
|
|
1045
|
+
class MakeResource < JSONAPI::Resource
|
1046
|
+
attribute :model
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
class WebPageResource < JSONAPI::Resource
|
1050
|
+
attribute :href
|
1051
|
+
attribute :link
|
1052
|
+
end
|
1053
|
+
|
1013
1054
|
module Api
|
1014
1055
|
module V1
|
1015
1056
|
class WriterResource < JSONAPI::Resource
|
@@ -1328,22 +1369,6 @@ module MyEngine
|
|
1328
1369
|
end
|
1329
1370
|
end
|
1330
1371
|
|
1331
|
-
warn 'start testing Name Collisions'
|
1332
|
-
# The name collisions only emmit warnings. Exceptions would change the flow of the tests
|
1333
|
-
|
1334
|
-
class LinksResource < JSONAPI::Resource
|
1335
|
-
end
|
1336
|
-
|
1337
|
-
class BadlyNamedAttributesResource < JSONAPI::Resource
|
1338
|
-
attributes :type, :href, :links
|
1339
|
-
|
1340
|
-
has_many :links
|
1341
|
-
has_one :href
|
1342
|
-
has_one :id
|
1343
|
-
has_many :types
|
1344
|
-
end
|
1345
|
-
warn 'end testing Name Collisions'
|
1346
|
-
|
1347
1372
|
### PORO Data - don't do this in a production app
|
1348
1373
|
$breed_data = BreedData.new
|
1349
1374
|
$breed_data.add(Breed.new(0, 'persian'))
|
data/test/fixtures/vehicles.yml
CHANGED
@@ -2,15 +2,16 @@ Miata:
|
|
2
2
|
id: 1
|
3
3
|
type: Car
|
4
4
|
make: Mazda
|
5
|
-
|
5
|
+
model: Miata MX5
|
6
6
|
drive_layout: Front Engine RWD
|
7
7
|
serial_number: 32432adfsfdysua
|
8
8
|
person_id: 1
|
9
|
+
|
9
10
|
Launch20:
|
10
11
|
id: 2
|
11
12
|
type: Boat
|
12
13
|
make: Chris-Craft
|
13
|
-
|
14
|
+
model: Launch 20
|
14
15
|
length_at_water_line: 15.5ft
|
15
16
|
serial_number: 434253JJJSD
|
16
17
|
person_id: 1
|
@@ -910,4 +910,17 @@ class RequestTest < ActionDispatch::IntegrationTest
|
|
910
910
|
ensure
|
911
911
|
JSONAPI.configuration.allow_sort = true
|
912
912
|
end
|
913
|
+
|
914
|
+
def test_getting_different_resources_when_sti
|
915
|
+
get '/vehicles'
|
916
|
+
assert_equal 200, status
|
917
|
+
types = json_response['data'].map{|r| r['type']}.sort
|
918
|
+
assert_array_equals ['boats', 'cars'], types
|
919
|
+
end
|
920
|
+
|
921
|
+
def test_getting_resource_with_correct_type_when_sti
|
922
|
+
get '/vehicles/1'
|
923
|
+
assert_equal 200, status
|
924
|
+
assert_equal 'cars', json_response['data']['type']
|
925
|
+
end
|
913
926
|
end
|
data/test/test_helper.rb
CHANGED
@@ -39,6 +39,7 @@ class TestApp < Rails::Application
|
|
39
39
|
#Raise errors on unsupported parameters
|
40
40
|
config.action_controller.action_on_unpermitted_parameters = :raise
|
41
41
|
|
42
|
+
ActiveRecord::Schema.verbose = false
|
42
43
|
config.active_record.schema_format = :none
|
43
44
|
config.active_support.test_order = :random
|
44
45
|
|
@@ -104,7 +105,6 @@ module Pets
|
|
104
105
|
end
|
105
106
|
|
106
107
|
class CatResource < JSONAPI::Resource
|
107
|
-
attribute :id
|
108
108
|
attribute :name
|
109
109
|
attribute :breed
|
110
110
|
|
@@ -137,8 +137,9 @@ TestApp.routes.draw do
|
|
137
137
|
jsonapi_resources :pictures
|
138
138
|
jsonapi_resources :documents
|
139
139
|
jsonapi_resources :products
|
140
|
-
|
141
|
-
|
140
|
+
jsonapi_resources :vehicles
|
141
|
+
jsonapi_resources :cars
|
142
|
+
jsonapi_resources :boats
|
142
143
|
|
143
144
|
namespace :api do
|
144
145
|
namespace :v1 do
|
@@ -184,8 +184,8 @@ class PagedPaginatorTest < ActiveSupport::TestCase
|
|
184
184
|
assert_equal 5, links_params['first']['size']
|
185
185
|
assert_equal 1, links_params['first']['number']
|
186
186
|
|
187
|
-
assert_equal 5, links_params['
|
188
|
-
assert_equal 3, links_params['
|
187
|
+
assert_equal 5, links_params['prev']['size']
|
188
|
+
assert_equal 3, links_params['prev']['number']
|
189
189
|
|
190
190
|
assert_equal 5, links_params['next']['size']
|
191
191
|
assert_equal 5, links_params['next']['number']
|
@@ -210,8 +210,8 @@ class PagedPaginatorTest < ActiveSupport::TestCase
|
|
210
210
|
assert_equal 5, links_params['first']['size']
|
211
211
|
assert_equal 1, links_params['first']['number']
|
212
212
|
|
213
|
-
assert_equal 5, links_params['
|
214
|
-
assert_equal 9, links_params['
|
213
|
+
assert_equal 5, links_params['prev']['size']
|
214
|
+
assert_equal 9, links_params['prev']['number']
|
215
215
|
|
216
216
|
assert_equal 5, links_params['last']['size']
|
217
217
|
assert_equal 10, links_params['last']['number']
|
@@ -233,8 +233,8 @@ class PagedPaginatorTest < ActiveSupport::TestCase
|
|
233
233
|
assert_equal 5, links_params['first']['size']
|
234
234
|
assert_equal 1, links_params['first']['number']
|
235
235
|
|
236
|
-
assert_equal 5, links_params['
|
237
|
-
assert_equal 10, links_params['
|
236
|
+
assert_equal 5, links_params['prev']['size']
|
237
|
+
assert_equal 10, links_params['prev']['number']
|
238
238
|
|
239
239
|
assert_equal 5, links_params['last']['size']
|
240
240
|
assert_equal 10, links_params['last']['number']
|
@@ -16,7 +16,6 @@ class NoMatchAbstractResource < JSONAPI::Resource
|
|
16
16
|
end
|
17
17
|
|
18
18
|
class CatResource < JSONAPI::Resource
|
19
|
-
attribute :id
|
20
19
|
attribute :name
|
21
20
|
attribute :breed
|
22
21
|
|
@@ -111,7 +110,7 @@ class ResourceTest < ActiveSupport::TestCase
|
|
111
110
|
|
112
111
|
def test_find_with_customized_base_records
|
113
112
|
author = Person.find(1)
|
114
|
-
posts = ArticleResource.find([], context: author).map(&:
|
113
|
+
posts = ArticleResource.find([], context: author).map(&:_model)
|
115
114
|
|
116
115
|
assert(posts.include?(Post.find(1)))
|
117
116
|
refute(posts.include?(Post.find(3)))
|
@@ -123,10 +122,10 @@ class ResourceTest < ActiveSupport::TestCase
|
|
123
122
|
refute(preferences == nil)
|
124
123
|
author.update! preferences: preferences
|
125
124
|
author_resource = PersonResource.new(author, nil)
|
126
|
-
assert_equal(author_resource.preferences.
|
125
|
+
assert_equal(author_resource.preferences._model, preferences)
|
127
126
|
|
128
127
|
author_resource = PersonWithCustomRecordsForResource.new(author, nil)
|
129
|
-
assert_equal(author_resource.preferences.
|
128
|
+
assert_equal(author_resource.preferences._model, :records_for)
|
130
129
|
|
131
130
|
author_resource = PersonWithCustomRecordsForErrorResource.new(author, nil)
|
132
131
|
assert_raises PersonWithCustomRecordsForErrorResource::AuthorizationError do
|
@@ -165,11 +164,11 @@ class ResourceTest < ActiveSupport::TestCase
|
|
165
164
|
def test_find_by_key_with_customized_base_records
|
166
165
|
author = Person.find(1)
|
167
166
|
|
168
|
-
post = ArticleResource.find_by_key(1, context: author).
|
167
|
+
post = ArticleResource.find_by_key(1, context: author)._model
|
169
168
|
assert_equal(post, Post.find(1))
|
170
169
|
|
171
170
|
assert_raises JSONAPI::Exceptions::RecordNotFound do
|
172
|
-
ArticleResource.find_by_key(3, context: author).
|
171
|
+
ArticleResource.find_by_key(3, context: author)._model
|
173
172
|
end
|
174
173
|
end
|
175
174
|
|
@@ -221,7 +220,7 @@ class ResourceTest < ActiveSupport::TestCase
|
|
221
220
|
|
222
221
|
def test_to_many_relationship_sorts
|
223
222
|
post_resource = PostResource.new(Post.find(1), nil)
|
224
|
-
comment_ids = post_resource.comments.map{|c| c.
|
223
|
+
comment_ids = post_resource.comments.map{|c| c._model.id }
|
225
224
|
assert_equal [1,2], comment_ids
|
226
225
|
|
227
226
|
# define apply_filters method on post resource to not respect filters
|
@@ -233,7 +232,7 @@ class ResourceTest < ActiveSupport::TestCase
|
|
233
232
|
end
|
234
233
|
end
|
235
234
|
|
236
|
-
sorted_comment_ids = post_resource.comments(sort_criteria: [{ field: 'id', direction: :desc}]).map{|c| c.
|
235
|
+
sorted_comment_ids = post_resource.comments(sort_criteria: [{ field: 'id', direction: :desc}]).map{|c| c._model.id }
|
237
236
|
assert_equal [2,1], sorted_comment_ids
|
238
237
|
|
239
238
|
ensure
|
@@ -362,4 +361,90 @@ class ResourceTest < ActiveSupport::TestCase
|
|
362
361
|
key_type nil
|
363
362
|
end
|
364
363
|
end
|
364
|
+
|
365
|
+
def test_id_attr_deprecation
|
366
|
+
_out, err = capture_io do
|
367
|
+
eval <<-CODE
|
368
|
+
class ProblemResource < JSONAPI::Resource
|
369
|
+
attribute :id
|
370
|
+
end
|
371
|
+
CODE
|
372
|
+
end
|
373
|
+
assert_match /DEPRECATION WARNING: Id without format is no longer supported. Please remove ids from attributes, or specify a format./, err
|
374
|
+
end
|
375
|
+
|
376
|
+
def test_id_attr_with_format
|
377
|
+
_out, err = capture_io do
|
378
|
+
eval <<-CODE
|
379
|
+
class NotProblemResource < JSONAPI::Resource
|
380
|
+
attribute :id, format: :string
|
381
|
+
end
|
382
|
+
CODE
|
383
|
+
end
|
384
|
+
assert_equal "", err
|
385
|
+
end
|
386
|
+
|
387
|
+
def test_links_resource_warning
|
388
|
+
_out, err = capture_io do
|
389
|
+
eval "class LinksResource < JSONAPI::Resource; end"
|
390
|
+
end
|
391
|
+
assert_match /LinksResource` is a reserved resource name/, err
|
392
|
+
end
|
393
|
+
|
394
|
+
def test_reserved_key_warnings
|
395
|
+
_out, err = capture_io do
|
396
|
+
eval <<-CODE
|
397
|
+
class BadlyNamedAttributesResource < JSONAPI::Resource
|
398
|
+
attributes :type
|
399
|
+
end
|
400
|
+
CODE
|
401
|
+
end
|
402
|
+
assert_match /`type` is a reserved key in ./, err
|
403
|
+
end
|
404
|
+
|
405
|
+
def test_reserved_relationship_warnings
|
406
|
+
%w(id type).each do |key|
|
407
|
+
_out, err = capture_io do
|
408
|
+
eval <<-CODE
|
409
|
+
class BadlyNamedAttributesResource < JSONAPI::Resource
|
410
|
+
has_one :#{key}
|
411
|
+
end
|
412
|
+
CODE
|
413
|
+
end
|
414
|
+
assert_match /`#{key}` is a reserved relationship name in ./, err
|
415
|
+
end
|
416
|
+
%w(types ids).each do |key|
|
417
|
+
_out, err = capture_io do
|
418
|
+
eval <<-CODE
|
419
|
+
class BadlyNamedAttributesResource < JSONAPI::Resource
|
420
|
+
has_many :#{key}
|
421
|
+
end
|
422
|
+
CODE
|
423
|
+
end
|
424
|
+
assert_match /`#{key}` is a reserved relationship name in ./, err
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
def test_abstract_warning
|
429
|
+
_out, err = capture_io do
|
430
|
+
eval <<-CODE
|
431
|
+
class NoModelResource < JSONAPI::Resource
|
432
|
+
end
|
433
|
+
NoModelResource._model_class
|
434
|
+
CODE
|
435
|
+
end
|
436
|
+
assert_match "[MODEL NOT FOUND] Model could not be found for ResourceTest::NoModelResource. If this a base Resource declare it as abstract.\n", err
|
437
|
+
end
|
438
|
+
|
439
|
+
def test_no_warning_when_abstract
|
440
|
+
_out, err = capture_io do
|
441
|
+
eval <<-CODE
|
442
|
+
class NoModelAbstractResource < JSONAPI::Resource
|
443
|
+
abstract
|
444
|
+
end
|
445
|
+
NoModelAbstractResource._model_class
|
446
|
+
CODE
|
447
|
+
end
|
448
|
+
assert_match "", err
|
449
|
+
end
|
365
450
|
end
|
@@ -88,7 +88,7 @@ class PolymorphismTest < ActionDispatch::IntegrationTest
|
|
88
88
|
},
|
89
89
|
attributes: {
|
90
90
|
make: 'Mazda',
|
91
|
-
|
91
|
+
model: 'Miata MX5',
|
92
92
|
driveLayout: 'Front Engine RWD',
|
93
93
|
serialNumber: '32432adfsfdysua'
|
94
94
|
},
|
@@ -109,7 +109,7 @@ class PolymorphismTest < ActionDispatch::IntegrationTest
|
|
109
109
|
},
|
110
110
|
attributes: {
|
111
111
|
make: 'Chris-Craft',
|
112
|
-
|
112
|
+
model: 'Launch 20',
|
113
113
|
lengthAtWaterLine: '15.5ft',
|
114
114
|
serialNumber: '434253JJJSD'
|
115
115
|
},
|
@@ -1736,4 +1736,139 @@ class SerializerTest < ActionDispatch::IntegrationTest
|
|
1736
1736
|
serialized
|
1737
1737
|
)
|
1738
1738
|
end
|
1739
|
+
|
1740
|
+
def test_serialize_model_attr
|
1741
|
+
@make = Make.first
|
1742
|
+
serialized = JSONAPI::ResourceSerializer.new(
|
1743
|
+
MakeResource,
|
1744
|
+
).serialize_to_hash(MakeResource.new(@make, nil))
|
1745
|
+
|
1746
|
+
assert_hash_equals(
|
1747
|
+
{
|
1748
|
+
"model" => "A model attribute"
|
1749
|
+
},
|
1750
|
+
serialized[:data]["attributes"]
|
1751
|
+
)
|
1752
|
+
end
|
1753
|
+
|
1754
|
+
def test_confusingly_named_attrs
|
1755
|
+
@wp = WebPage.first
|
1756
|
+
serialized = JSONAPI::ResourceSerializer.new(
|
1757
|
+
WebPageResource,
|
1758
|
+
).serialize_to_hash(WebPageResource.new(@wp, nil))
|
1759
|
+
|
1760
|
+
assert_hash_equals(
|
1761
|
+
{
|
1762
|
+
:data=>{
|
1763
|
+
"id"=>"#{@wp.id}",
|
1764
|
+
"type"=>"webPages",
|
1765
|
+
"links"=>{
|
1766
|
+
:self=>"/webPages/#{@wp.id}"
|
1767
|
+
},
|
1768
|
+
"attributes"=>{
|
1769
|
+
"href"=>"http://example.com",
|
1770
|
+
"link"=>"http://link.example.com"
|
1771
|
+
}
|
1772
|
+
}
|
1773
|
+
},
|
1774
|
+
serialized
|
1775
|
+
)
|
1776
|
+
end
|
1777
|
+
|
1778
|
+
def test_questionable_has_one
|
1779
|
+
# has_one
|
1780
|
+
out, err = capture_io do
|
1781
|
+
eval <<-CODE
|
1782
|
+
class ::Questionable < ActiveRecord::Base
|
1783
|
+
has_one :link
|
1784
|
+
has_one :href
|
1785
|
+
end
|
1786
|
+
class ::QuestionableResource < JSONAPI::Resource
|
1787
|
+
model_name '::Questionable'
|
1788
|
+
has_one :link
|
1789
|
+
has_one :href
|
1790
|
+
end
|
1791
|
+
cn = ::Questionable.new id: 1
|
1792
|
+
puts JSONAPI::ResourceSerializer.new(
|
1793
|
+
::QuestionableResource,
|
1794
|
+
).serialize_to_hash(::QuestionableResource.new(cn, nil))
|
1795
|
+
CODE
|
1796
|
+
end
|
1797
|
+
assert err.blank?
|
1798
|
+
assert_equal(
|
1799
|
+
{
|
1800
|
+
:data=>{
|
1801
|
+
"id"=>"1",
|
1802
|
+
"type"=>"questionables",
|
1803
|
+
"links"=>{
|
1804
|
+
:self=>"/questionables/1"
|
1805
|
+
},
|
1806
|
+
"relationships"=>{
|
1807
|
+
"link"=>{
|
1808
|
+
:links=>{
|
1809
|
+
:self=>"/questionables/1/relationships/link",
|
1810
|
+
:related=>"/questionables/1/link"
|
1811
|
+
}
|
1812
|
+
},
|
1813
|
+
"href"=>{
|
1814
|
+
:links=>{
|
1815
|
+
:self=>"/questionables/1/relationships/href",
|
1816
|
+
:related=>"/questionables/1/href"
|
1817
|
+
}
|
1818
|
+
}
|
1819
|
+
}
|
1820
|
+
}
|
1821
|
+
}.to_s,
|
1822
|
+
out.strip
|
1823
|
+
)
|
1824
|
+
end
|
1825
|
+
|
1826
|
+
def test_questionable_has_many
|
1827
|
+
# has_one
|
1828
|
+
out, err = capture_io do
|
1829
|
+
eval <<-CODE
|
1830
|
+
class ::Questionable2 < ActiveRecord::Base
|
1831
|
+
self.table_name = 'questionables'
|
1832
|
+
has_many :links
|
1833
|
+
has_many :hrefs
|
1834
|
+
end
|
1835
|
+
class ::Questionable2Resource < JSONAPI::Resource
|
1836
|
+
model_name '::Questionable2'
|
1837
|
+
has_many :links
|
1838
|
+
has_many :hrefs
|
1839
|
+
end
|
1840
|
+
cn = ::Questionable2.new id: 1
|
1841
|
+
puts JSONAPI::ResourceSerializer.new(
|
1842
|
+
::Questionable2Resource,
|
1843
|
+
).serialize_to_hash(::Questionable2Resource.new(cn, nil))
|
1844
|
+
CODE
|
1845
|
+
end
|
1846
|
+
assert err.blank?
|
1847
|
+
assert_equal(
|
1848
|
+
{
|
1849
|
+
:data=>{
|
1850
|
+
"id"=>"1",
|
1851
|
+
"type"=>"questionable2s",
|
1852
|
+
"links"=>{
|
1853
|
+
:self=>"/questionable2s/1"
|
1854
|
+
},
|
1855
|
+
"relationships"=>{
|
1856
|
+
"links"=>{
|
1857
|
+
:links=>{
|
1858
|
+
:self=>"/questionable2s/1/relationships/links",
|
1859
|
+
:related=>"/questionable2s/1/links"
|
1860
|
+
}
|
1861
|
+
},
|
1862
|
+
"hrefs"=>{
|
1863
|
+
:links=>{
|
1864
|
+
:self=>"/questionable2s/1/relationships/hrefs",
|
1865
|
+
:related=>"/questionable2s/1/hrefs"
|
1866
|
+
}
|
1867
|
+
}
|
1868
|
+
}
|
1869
|
+
}
|
1870
|
+
}.to_s,
|
1871
|
+
out.strip
|
1872
|
+
)
|
1873
|
+
end
|
1739
1874
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jsonapi-resources
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Gebhardt
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-10-
|
12
|
+
date: 2015-10-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -171,6 +171,7 @@ files:
|
|
171
171
|
- test/fixtures/hair_cuts.yml
|
172
172
|
- test/fixtures/iso_currencies.yml
|
173
173
|
- test/fixtures/line_items.yml
|
174
|
+
- test/fixtures/makes.yml
|
174
175
|
- test/fixtures/moons.yml
|
175
176
|
- test/fixtures/numeros_telefone.yml
|
176
177
|
- test/fixtures/order_flags.yml
|
@@ -186,6 +187,7 @@ files:
|
|
186
187
|
- test/fixtures/sections.yml
|
187
188
|
- test/fixtures/tags.yml
|
188
189
|
- test/fixtures/vehicles.yml
|
190
|
+
- test/fixtures/web_pages.yml
|
189
191
|
- test/helpers/assertions.rb
|
190
192
|
- test/helpers/functional_helpers.rb
|
191
193
|
- test/helpers/value_matchers.rb
|
@@ -248,6 +250,7 @@ test_files:
|
|
248
250
|
- test/fixtures/hair_cuts.yml
|
249
251
|
- test/fixtures/iso_currencies.yml
|
250
252
|
- test/fixtures/line_items.yml
|
253
|
+
- test/fixtures/makes.yml
|
251
254
|
- test/fixtures/moons.yml
|
252
255
|
- test/fixtures/numeros_telefone.yml
|
253
256
|
- test/fixtures/order_flags.yml
|
@@ -263,6 +266,7 @@ test_files:
|
|
263
266
|
- test/fixtures/sections.yml
|
264
267
|
- test/fixtures/tags.yml
|
265
268
|
- test/fixtures/vehicles.yml
|
269
|
+
- test/fixtures/web_pages.yml
|
266
270
|
- test/helpers/assertions.rb
|
267
271
|
- test/helpers/functional_helpers.rb
|
268
272
|
- test/helpers/value_matchers.rb
|