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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2d31c91126186e543683030f0b2c2a0fb513d64e
4
- data.tar.gz: 8bd319a9b582d306093f9cfaf82733f5edeb7aa1
3
+ metadata.gz: 6700d2a30e18dc6391ba4aa8bc0ecdd517e2fdfb
4
+ data.tar.gz: ad330dfe0d8fdc5da07d7ee40db08e892293296d
5
5
  SHA512:
6
- metadata.gz: d4b2634d12eba8207a651bdb60a42e82bb85136d7098feaaf70821ea58c8200636b8b4e480a423b8ab8e1af1f2f8837cb33fe307adf55843c6da770e4c02e7f9
7
- data.tar.gz: ebd5e529dd9662930ee305309cba83324325e090abdda8fcb7cf6a8cc204b437fde534d6de58d5815c5c1278efa9c527b4ea6ce3113940fcc651b00c5015bd37
6
+ metadata.gz: 893a1947cd78da813d77e69c92dafbaac07b50c8dab2ab495ebdf49da50c082f8123c99361cf95793e606dd4916d140401566a4465829bf137aa1f86144589db
7
+ data.tar.gz: d00fb87cc0c3a787d28fb68bc1d8072b6dc30143108dc3672770b19704430126511bf0aa73d2b86434546812ff6a4d194d710eb521a6f9d88840d8faeeef59d6
data/.travis.yml CHANGED
@@ -1,4 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
+ - 1.9.3
3
4
  - 2.0.0
4
- - 2.1.1
5
+ - 2.1
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. However 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.
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
- Here's an example that defers the `find` operation to a `current_user` set on the `context` option:
265
+ ###### Customizing base records for finder methods
266
266
 
267
- ```ruby
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
- filter :name
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.find(attrs, options = {})
275
+ def self.records(options = {})
276
276
  context = options[:context]
277
- authors = context.current_user.find_authors(attrs)
277
+ context.current_user.posts
278
+ end
279
+ end
280
+ ```
278
281
 
279
- return authors.map do |author|
280
- self.new(author)
281
- end
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
- ##### Customizing base records for finder methods
308
+ ###### Override finder methods
287
309
 
288
- 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.
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
- For example to allow a user to only retrieve his own posts you can do the following:
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 PostResource < JSONAPI::Resource
294
- attribute :id, :title, :body
315
+ class AuthorResource < JSONAPI::Resource
316
+ attributes :id, :name
317
+ model_name 'Person'
318
+ has_many :posts
295
319
 
296
- def self.records(options = {})
320
+ filter :name
321
+
322
+ def self.find(filters, options = {})
297
323
  context = options[:context]
298
- context.current_user.posts
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
  ```
@@ -97,12 +97,7 @@ end
97
97
  class DefaultValueFormatter < JSONAPI::ValueFormatter
98
98
  class << self
99
99
  def format(raw_value, context)
100
- case raw_value
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
@@ -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
- type_resource = self.class.resource_for(underscored_type)
80
- if type_resource.nil? || !(@resource_klass._type == underscored_type ||
81
- @resource_klass._has_association?(underscored_type))
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)}
@@ -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
- where_filters = {}
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
- where_filters["#{filter}.#{_associations[filter].primary_key}"] = value
236
+ records = apply_filter(records, "#{filter}.#{_associations[filter].primary_key}", value)
232
237
  else
233
- where_filters["#{_associations[filter].foreign_key}"] = value
238
+ records = apply_filter(records, "#{_associations[filter].foreign_key}", value)
234
239
  end
235
240
  else
236
- where_filters[filter] = value
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(options).where(where_filters).order(order_options).includes(includes).each do |model|
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(:id).map(&:to_s)).first
268
+ key = (keys - _models.pluck(_primary_key).map(&:to_s)).first
264
269
  raise JSONAPI::Exceptions::RecordNotFound.new(key)
265
270
  end
266
271
 
@@ -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
- nil
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
@@ -1,5 +1,5 @@
1
1
  module JSONAPI
2
2
  module Resources
3
- VERSION = "0.0.13"
3
+ VERSION = "0.0.14"
4
4
  end
5
5
  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 '50.58', json_response['expense_entries']['cost']
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 '50.58', json_response['expenseEntries']['cost']
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 '50.58', json_response['expense-entries']['cost']
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(attrs, options = {})
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, context: nil)
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: '12.05',
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.13
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: 2014-12-06 00:00:00.000000000 Z
12
+ date: 2015-01-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler