jsonapi-resources 0.0.13 → 0.0.14
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/.travis.yml +2 -1
- data/README.md +50 -20
- data/lib/jsonapi/formatter.rb +1 -6
- data/lib/jsonapi/request.rb +8 -4
- data/lib/jsonapi/resource.rb +11 -6
- data/lib/jsonapi/resource_for.rb +7 -3
- data/lib/jsonapi/resources/version.rb +1 -1
- data/test/controllers/controller_test.rb +21 -3
- data/test/fixtures/active_record.rb +45 -2
- data/test/test_helper.rb +3 -0
- data/test/unit/serializer/serializer_test.rb +43 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6700d2a30e18dc6391ba4aa8bc0ecdd517e2fdfb
|
4
|
+
data.tar.gz: ad330dfe0d8fdc5da07d7ee40db08e892293296d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 893a1947cd78da813d77e69c92dafbaac07b50c8dab2ab495ebdf49da50c082f8123c99361cf95793e606dd4916d140401566a4465829bf137aa1f86144589db
|
7
|
+
data.tar.gz: d00fb87cc0c3a787d28fb68bc1d8072b6dc30143108dc3672770b19704430126511bf0aa73d2b86434546812ff6a4d194d710eb521a6f9d88840d8faeeef59d6
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -260,42 +260,72 @@ end
|
|
260
260
|
|
261
261
|
##### Finders
|
262
262
|
|
263
|
-
Basic finding by filters is supported by resources.
|
263
|
+
Basic finding by filters is supported by resources. This is implemented in the `find`, `find_by_key` and `find_by_keys` finder methods. Currently this is implemented for ActiveRecord based resources. The finder methods rely on the `records` method to get an Arel relation. It is therefore possible to override `records` to affect the three find related methods.
|
264
264
|
|
265
|
-
|
265
|
+
###### Customizing base records for finder methods
|
266
266
|
|
267
|
-
|
268
|
-
class AuthorResource < JSONAPI::Resource
|
269
|
-
attributes :id, :name
|
270
|
-
model_name 'Person'
|
271
|
-
has_many :posts
|
267
|
+
If you need to change the base records on which `find`, `find_by_key` and `find_by_keys` operate, you can override the `records` method on the resource class.
|
272
268
|
|
273
|
-
|
269
|
+
For example to allow a user to only retrieve his own posts you can do the following:
|
270
|
+
|
271
|
+
```ruby
|
272
|
+
class PostResource < JSONAPI::Resource
|
273
|
+
attribute :id, :title, :body
|
274
274
|
|
275
|
-
def self.
|
275
|
+
def self.records(options = {})
|
276
276
|
context = options[:context]
|
277
|
-
|
277
|
+
context.current_user.posts
|
278
|
+
end
|
279
|
+
end
|
280
|
+
```
|
278
281
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
+
###### Applying Filters
|
283
|
+
|
284
|
+
The `apply_filter` method is called to apply each filter to the Arel relation. You may override this method to gain control over how the filters are applied to the Arel relation.
|
285
|
+
|
286
|
+
For example to change how a
|
287
|
+
|
288
|
+
```ruby
|
289
|
+
def apply_filter(records, filter, value)
|
290
|
+
case filter
|
291
|
+
when :visibility
|
292
|
+
records.where('users.publicly_visible = ?', value == :public)
|
293
|
+
when :last_name, :first_name, :name
|
294
|
+
if value.is_a?(Array)
|
295
|
+
value.each do |val|
|
296
|
+
records = records.where(_model_class.arel_table[filter].matches(val))
|
297
|
+
end
|
298
|
+
return records
|
299
|
+
else
|
300
|
+
records.where(_model_class.arel_table[filter].matches(value))
|
301
|
+
end
|
302
|
+
else
|
303
|
+
return super(records, filter, value)
|
282
304
|
end
|
283
305
|
end
|
284
306
|
```
|
285
307
|
|
286
|
-
|
308
|
+
###### Override finder methods
|
287
309
|
|
288
|
-
|
310
|
+
Finally if you have more complex requirements for finding you can override the `find`, `find_by_key` and `find_by_keys` methods on the resource class.
|
289
311
|
|
290
|
-
|
312
|
+
Here's an example that defers the `find` operation to a `current_user` set on the `context` option:
|
291
313
|
|
292
314
|
```ruby
|
293
|
-
class
|
294
|
-
|
315
|
+
class AuthorResource < JSONAPI::Resource
|
316
|
+
attributes :id, :name
|
317
|
+
model_name 'Person'
|
318
|
+
has_many :posts
|
295
319
|
|
296
|
-
|
320
|
+
filter :name
|
321
|
+
|
322
|
+
def self.find(filters, options = {})
|
297
323
|
context = options[:context]
|
298
|
-
context.current_user.
|
324
|
+
authors = context.current_user.find_authors(filters)
|
325
|
+
|
326
|
+
return authors.map do |author|
|
327
|
+
self.new(author)
|
328
|
+
end
|
299
329
|
end
|
300
330
|
end
|
301
331
|
```
|
data/lib/jsonapi/formatter.rb
CHANGED
@@ -97,12 +97,7 @@ end
|
|
97
97
|
class DefaultValueFormatter < JSONAPI::ValueFormatter
|
98
98
|
class << self
|
99
99
|
def format(raw_value, context)
|
100
|
-
|
101
|
-
when String, Integer
|
102
|
-
return raw_value
|
103
|
-
else
|
104
|
-
return raw_value.to_s
|
105
|
-
end
|
100
|
+
raw_value
|
106
101
|
end
|
107
102
|
end
|
108
103
|
end
|
data/lib/jsonapi/request.rb
CHANGED
@@ -20,7 +20,7 @@ module JSONAPI
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def setup(params)
|
23
|
-
@resource_klass ||= self.class.resource_for(params[:controller]) if params[:controller]
|
23
|
+
@resource_klass ||= self.class.resource_for(params[:controller].split('/').last) if params[:controller]
|
24
24
|
|
25
25
|
unless params.nil?
|
26
26
|
case params[:action]
|
@@ -76,10 +76,14 @@ module JSONAPI
|
|
76
76
|
fields.each do |type, values|
|
77
77
|
underscored_type = unformat_key(type)
|
78
78
|
fields[type] = []
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
begin
|
80
|
+
type_resource = self.class.resource_for(underscored_type)
|
81
|
+
rescue NameError
|
82
82
|
@errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
|
83
|
+
end
|
84
|
+
if type_resource.nil? || !(@resource_klass._type == underscored_type ||
|
85
|
+
@resource_klass._has_association?(underscored_type))
|
86
|
+
@errors.concat(JSONAPI::Exceptions::InvalidResource.new(type).errors)
|
83
87
|
else
|
84
88
|
unless values.nil?
|
85
89
|
valid_fields = type_resource.fields.collect {|key| format_key(key)}
|
data/lib/jsonapi/resource.rb
CHANGED
@@ -217,29 +217,34 @@ module JSONAPI
|
|
217
217
|
_associations.keys | _attributes.keys
|
218
218
|
end
|
219
219
|
|
220
|
+
def apply_filter(records, filter, value)
|
221
|
+
records.where(filter => value)
|
222
|
+
end
|
223
|
+
|
220
224
|
# Override this method if you have more complex requirements than this basic find method provides
|
221
225
|
def find(filters, options = {})
|
222
226
|
context = options[:context]
|
223
227
|
sort_params = options.fetch(:sort_params) { [] }
|
224
228
|
includes = []
|
225
|
-
|
229
|
+
|
230
|
+
records = records(options)
|
226
231
|
|
227
232
|
filters.each do |filter, value|
|
228
233
|
if _associations.include?(filter)
|
229
234
|
if _associations[filter].is_a?(JSONAPI::Association::HasMany)
|
230
235
|
includes.push(filter)
|
231
|
-
|
236
|
+
records = apply_filter(records, "#{filter}.#{_associations[filter].primary_key}", value)
|
232
237
|
else
|
233
|
-
|
238
|
+
records = apply_filter(records, "#{_associations[filter].foreign_key}", value)
|
234
239
|
end
|
235
240
|
else
|
236
|
-
|
241
|
+
records = apply_filter(records, filter, value)
|
237
242
|
end
|
238
243
|
end
|
239
244
|
|
240
245
|
resources = []
|
241
246
|
order_options = construct_order_options(sort_params)
|
242
|
-
records
|
247
|
+
records.order(order_options).includes(includes).each do |model|
|
243
248
|
resources.push self.new(model, context)
|
244
249
|
end
|
245
250
|
|
@@ -260,7 +265,7 @@ module JSONAPI
|
|
260
265
|
_models = records(options).where({_primary_key => keys})
|
261
266
|
|
262
267
|
unless _models.length == keys.length
|
263
|
-
key = (keys - _models.pluck(
|
268
|
+
key = (keys - _models.pluck(_primary_key).map(&:to_s)).first
|
264
269
|
raise JSONAPI::Exceptions::RecordNotFound.new(key)
|
265
270
|
end
|
266
271
|
|
data/lib/jsonapi/resource_for.rb
CHANGED
@@ -11,15 +11,19 @@ module JSONAPI
|
|
11
11
|
resource_name = JSONAPI::Resource._resource_name_from_type(type)
|
12
12
|
Object.const_get resource_name if resource_name
|
13
13
|
rescue NameError
|
14
|
-
|
14
|
+
raise NameError, "JSONAPI: Could not find resource '#{type}'. (Class #{resource_name} not found)"
|
15
15
|
end
|
16
16
|
else
|
17
17
|
def resource_for(type)
|
18
18
|
resource_name = JSONAPI::Resource._resource_name_from_type(type)
|
19
|
-
resource_name.safe_constantize if resource_name
|
19
|
+
resource = resource_name.safe_constantize if resource_name
|
20
|
+
if resource.nil?
|
21
|
+
raise NameError, "JSONAPI: Could not find resource '#{type}'. (Class #{resource_name} not found)"
|
22
|
+
end
|
23
|
+
resource
|
20
24
|
end
|
21
25
|
end
|
22
26
|
# :nocov:
|
23
27
|
end
|
24
28
|
end
|
25
|
-
end
|
29
|
+
end
|
@@ -1106,7 +1106,7 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
|
|
1106
1106
|
assert json_response['expense_entries'].is_a?(Hash)
|
1107
1107
|
assert_equal 3, json_response['expense_entries']['links']['employee']
|
1108
1108
|
assert_equal 'USD', json_response['expense_entries']['links']['iso_currency']
|
1109
|
-
assert_equal
|
1109
|
+
assert_equal 50.58, json_response['expense_entries']['cost']
|
1110
1110
|
|
1111
1111
|
delete :destroy, {id: json_response['expense_entries']['id']}
|
1112
1112
|
assert_response :no_content
|
@@ -1133,7 +1133,7 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
|
|
1133
1133
|
assert json_response['expenseEntries'].is_a?(Hash)
|
1134
1134
|
assert_equal 3, json_response['expenseEntries']['links']['employee']
|
1135
1135
|
assert_equal 'USD', json_response['expenseEntries']['links']['isoCurrency']
|
1136
|
-
assert_equal
|
1136
|
+
assert_equal 50.58, json_response['expenseEntries']['cost']
|
1137
1137
|
|
1138
1138
|
delete :destroy, {id: json_response['expenseEntries']['id']}
|
1139
1139
|
assert_response :no_content
|
@@ -1160,7 +1160,7 @@ class ExpenseEntriesControllerTest < ActionController::TestCase
|
|
1160
1160
|
assert json_response['expense-entries'].is_a?(Hash)
|
1161
1161
|
assert_equal 3, json_response['expense-entries']['links']['employee']
|
1162
1162
|
assert_equal 'USD', json_response['expense-entries']['links']['iso-currency']
|
1163
|
-
assert_equal
|
1163
|
+
assert_equal 50.58, json_response['expense-entries']['cost']
|
1164
1164
|
|
1165
1165
|
delete :destroy, {id: json_response['expense-entries']['id']}
|
1166
1166
|
assert_response :no_content
|
@@ -1466,3 +1466,21 @@ class Api::V2::PreferencesControllerTest < ActionController::TestCase
|
|
1466
1466
|
assert_response :success
|
1467
1467
|
end
|
1468
1468
|
end
|
1469
|
+
|
1470
|
+
class FactsControllerTest < ActionController::TestCase
|
1471
|
+
def test_type_formatting
|
1472
|
+
get :show, {id: '1'}
|
1473
|
+
assert_response :success
|
1474
|
+
assert json_response['facts'].is_a?(Hash)
|
1475
|
+
assert_equal 'Jane Author', json_response['facts']['spouseName']
|
1476
|
+
assert_equal 'First man to run across Antartica.', json_response['facts']['bio']
|
1477
|
+
assert_equal 23.89/45.6, json_response['facts']['qualityRating']
|
1478
|
+
assert_equal 47000.56, json_response['facts']['salary']
|
1479
|
+
assert_equal '2013-08-07T20:25:00.000Z', json_response['facts']['dateTimeJoined']
|
1480
|
+
assert_equal '1965-06-30', json_response['facts']['birthday']
|
1481
|
+
assert_equal '2000-01-01T20:00:00Z', json_response['facts']['bedtime']
|
1482
|
+
assert_equal 'abc', json_response['facts']['photo']
|
1483
|
+
assert_equal false, json_response['facts']['cool']
|
1484
|
+
end
|
1485
|
+
end
|
1486
|
+
|
@@ -92,6 +92,19 @@ ActiveRecord::Schema.define do
|
|
92
92
|
t.integer :person_id
|
93
93
|
t.boolean :advanced_mode, default: false
|
94
94
|
end
|
95
|
+
|
96
|
+
create_table :facts, force: true do |t|
|
97
|
+
t.integer :person_id
|
98
|
+
t.string :spouse_name
|
99
|
+
t.text :bio
|
100
|
+
t.float :quality_rating
|
101
|
+
t.decimal :salary, :precision => 12, :scale => 2
|
102
|
+
t.datetime :date_time_joined
|
103
|
+
t.date :birthday
|
104
|
+
t.time :bedtime
|
105
|
+
t.binary :photo, limit: 1.kilobyte
|
106
|
+
t.boolean :cool
|
107
|
+
end
|
95
108
|
end
|
96
109
|
|
97
110
|
### MODELS
|
@@ -160,6 +173,9 @@ class Preferences < ActiveRecord::Base
|
|
160
173
|
has_many :friends, class_name: 'Person'
|
161
174
|
end
|
162
175
|
|
176
|
+
class Fact < ActiveRecord::Base
|
177
|
+
end
|
178
|
+
|
163
179
|
class Breed
|
164
180
|
|
165
181
|
def initialize(id = nil, name = nil)
|
@@ -236,6 +252,9 @@ end
|
|
236
252
|
class BreedsController < JSONAPI::ResourceController
|
237
253
|
end
|
238
254
|
|
255
|
+
class FactsController < JSONAPI::ResourceController
|
256
|
+
end
|
257
|
+
|
239
258
|
### CONTROLLERS
|
240
259
|
module Api
|
241
260
|
module V1
|
@@ -455,7 +474,7 @@ class BreedResource < JSONAPI::Resource
|
|
455
474
|
# This is unneeded, just here for testing
|
456
475
|
routing_options :param => :id
|
457
476
|
|
458
|
-
def self.find(
|
477
|
+
def self.find(filters, options = {})
|
459
478
|
breeds = []
|
460
479
|
$breed_data.breeds.values.each do |breed|
|
461
480
|
breeds.push(BreedResource.new(breed, options[:context]))
|
@@ -509,11 +528,24 @@ class PreferencesResource < JSONAPI::Resource
|
|
509
528
|
has_one :author, foreign_key: :person_id
|
510
529
|
has_many :friends
|
511
530
|
|
512
|
-
def self.find_by_key(key,
|
531
|
+
def self.find_by_key(key, options = {})
|
513
532
|
new(Preferences.first)
|
514
533
|
end
|
515
534
|
end
|
516
535
|
|
536
|
+
class FactResource < JSONAPI::Resource
|
537
|
+
attribute :id
|
538
|
+
attribute :spouse_name
|
539
|
+
attribute :bio
|
540
|
+
attribute :quality_rating
|
541
|
+
attribute :salary
|
542
|
+
attribute :date_time_joined
|
543
|
+
attribute :birthday
|
544
|
+
attribute :bedtime
|
545
|
+
attribute :photo
|
546
|
+
attribute :cool
|
547
|
+
end
|
548
|
+
|
517
549
|
warn 'start testing Name Collisions'
|
518
550
|
# The name collisions only emmit warnings. Exceptions would change the flow of the tests
|
519
551
|
|
@@ -685,3 +717,14 @@ betaz = Planet.create(name: 'Beta X', description: 'Newly discovered Planet Z',
|
|
685
717
|
betaw = Planet.create(name: 'Beta W', description: 'Newly discovered Planet W')
|
686
718
|
|
687
719
|
preference = Preferences.create
|
720
|
+
|
721
|
+
fact = Fact.create(spouse_name: 'Jane Author',
|
722
|
+
bio: 'First man to run across Antartica.',
|
723
|
+
quality_rating: 23.89/45.6,
|
724
|
+
salary: BigDecimal('47000.56'),
|
725
|
+
date_time_joined: DateTime.parse('2013-08-07 20:25:00 UTC +00:00'),
|
726
|
+
birthday: Date.parse('1965-06-30'),
|
727
|
+
bedtime: Time.parse('2000-01-01 20:00:00 UTC +00:00'),
|
728
|
+
photo: "abc",
|
729
|
+
cool: false
|
730
|
+
)
|
data/test/test_helper.rb
CHANGED
@@ -31,6 +31,8 @@ class TestApp < Rails::Application
|
|
31
31
|
config.session_store :cookie_store, key: 'session'
|
32
32
|
config.secret_key_base = 'secret'
|
33
33
|
|
34
|
+
ActiveSupport::JSON::Encoding.encode_big_decimal_as_string = false
|
35
|
+
|
34
36
|
#Raise errors on unsupported parameters
|
35
37
|
config.action_controller.action_on_unpermitted_parameters = :raise
|
36
38
|
end
|
@@ -53,6 +55,7 @@ TestApp.routes.draw do
|
|
53
55
|
jsonapi_resources :planet_types
|
54
56
|
jsonapi_resources :moons
|
55
57
|
jsonapi_resources :preferences
|
58
|
+
jsonapi_resources :facts
|
56
59
|
|
57
60
|
namespace :api do
|
58
61
|
namespace :v1 do
|
@@ -505,7 +505,7 @@ class SerializerTest < MiniTest::Unit::TestCase
|
|
505
505
|
expenseEntries: {
|
506
506
|
id: 1,
|
507
507
|
transactionDate: '04/15/2014',
|
508
|
-
cost:
|
508
|
+
cost: 12.05,
|
509
509
|
links: {
|
510
510
|
isoCurrency: 'USD',
|
511
511
|
employee: 3
|
@@ -602,4 +602,46 @@ class SerializerTest < MiniTest::Unit::TestCase
|
|
602
602
|
assert_match /\"planetType\":null/, json
|
603
603
|
assert_match /\"moons\":\[\]/, json
|
604
604
|
end
|
605
|
+
|
606
|
+
def test_serializer_booleans
|
607
|
+
JSONAPI.configuration.json_key_format = :underscored_key
|
608
|
+
|
609
|
+
preferences = PreferencesResource.new(Preferences.find(1))
|
610
|
+
|
611
|
+
assert_hash_equals(
|
612
|
+
{
|
613
|
+
preferences: {
|
614
|
+
id: 1,
|
615
|
+
advanced_mode: false,
|
616
|
+
links: {
|
617
|
+
author: nil,
|
618
|
+
friends: []
|
619
|
+
}
|
620
|
+
}
|
621
|
+
},
|
622
|
+
JSONAPI::ResourceSerializer.new(PreferencesResource).serialize_to_hash(preferences))
|
623
|
+
end
|
624
|
+
|
625
|
+
def test_serializer_data_types
|
626
|
+
JSONAPI.configuration.json_key_format = :underscored_key
|
627
|
+
|
628
|
+
facts = FactResource.new(Fact.find(1))
|
629
|
+
|
630
|
+
assert_hash_equals(
|
631
|
+
{
|
632
|
+
facts: {
|
633
|
+
id: 1,
|
634
|
+
spouse_name: 'Jane Author',
|
635
|
+
bio: 'First man to run across Antartica.',
|
636
|
+
quality_rating: 23.89/45.6,
|
637
|
+
salary: BigDecimal('47000.56', 30),
|
638
|
+
date_time_joined: DateTime.parse('2013-08-07 20:25:00 UTC +00:00'),
|
639
|
+
birthday: Date.parse('1965-06-30'),
|
640
|
+
bedtime: Time.parse('2000-01-01 20:00:00 UTC +00:00'), #DB seems to set the date to 2001-01-01 for time types
|
641
|
+
photo: "abc",
|
642
|
+
cool: false
|
643
|
+
}
|
644
|
+
},
|
645
|
+
JSONAPI::ResourceSerializer.new(FactResource).serialize_to_hash(facts))
|
646
|
+
end
|
605
647
|
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.0.
|
4
|
+
version: 0.0.14
|
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:
|
12
|
+
date: 2015-01-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|