jsonapi-resources 0.2.0 → 0.3.0.pre1

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.travis.yml +5 -2
  4. data/Gemfile +3 -1
  5. data/README.md +52 -13
  6. data/jsonapi-resources.gemspec +1 -1
  7. data/lib/jsonapi-resources.rb +1 -0
  8. data/lib/jsonapi/association.rb +1 -9
  9. data/lib/jsonapi/error_codes.rb +1 -0
  10. data/lib/jsonapi/exceptions.rb +9 -5
  11. data/lib/jsonapi/formatter.rb +9 -18
  12. data/lib/jsonapi/paginator.rb +4 -15
  13. data/lib/jsonapi/request.rb +26 -42
  14. data/lib/jsonapi/resource.rb +35 -45
  15. data/lib/jsonapi/resource_controller.rb +6 -32
  16. data/lib/jsonapi/resource_serializer.rb +62 -33
  17. data/lib/jsonapi/resources/version.rb +1 -1
  18. data/lib/jsonapi/routing_ext.rb +4 -4
  19. data/test/config/database.yml +2 -1
  20. data/test/controllers/controller_test.rb +200 -160
  21. data/test/fixtures/active_record.rb +44 -201
  22. data/test/fixtures/book_comments.yml +11 -0
  23. data/test/fixtures/books.yml +6 -0
  24. data/test/fixtures/comments.yml +17 -0
  25. data/test/fixtures/comments_tags.yml +20 -0
  26. data/test/fixtures/expense_entries.yml +13 -0
  27. data/test/fixtures/facts.yml +11 -0
  28. data/test/fixtures/iso_currencies.yml +17 -0
  29. data/test/fixtures/people.yml +24 -0
  30. data/test/fixtures/posts.yml +96 -0
  31. data/test/fixtures/posts_tags.yml +59 -0
  32. data/test/fixtures/preferences.yml +18 -0
  33. data/test/fixtures/sections.yml +8 -0
  34. data/test/fixtures/tags.yml +39 -0
  35. data/test/helpers/hash_helpers.rb +0 -7
  36. data/test/integration/requests/request_test.rb +86 -28
  37. data/test/integration/routes/routes_test.rb +14 -25
  38. data/test/test_helper.rb +41 -17
  39. data/test/unit/jsonapi_request/jsonapi_request_test.rb +152 -0
  40. data/test/unit/operation/operations_processor_test.rb +13 -2
  41. data/test/unit/resource/resource_test.rb +68 -13
  42. data/test/unit/serializer/serializer_test.rb +328 -220
  43. metadata +33 -6
  44. data/lib/jsonapi/resource_for.rb +0 -29
@@ -7,8 +7,8 @@ class RoutesTest < ActionDispatch::IntegrationTest
7
7
  {controller: 'posts', action: 'create'})
8
8
  end
9
9
 
10
- def test_routing_put
11
- assert_routing({path: '/posts/1', method: :put},
10
+ def test_routing_patch
11
+ assert_routing({path: '/posts/1', method: :patch},
12
12
  {controller: 'posts', action: 'update', id: '1'})
13
13
  end
14
14
 
@@ -28,7 +28,7 @@ class RoutesTest < ActionDispatch::IntegrationTest
28
28
  end
29
29
 
30
30
  def test_routing_posts_links_author_update
31
- assert_routing({path: '/posts/1/links/author', method: :put},
31
+ assert_routing({path: '/posts/1/links/author', method: :patch},
32
32
  {controller: 'posts', action: 'update_association', post_id: '1', association: 'author'})
33
33
  end
34
34
 
@@ -48,26 +48,10 @@ class RoutesTest < ActionDispatch::IntegrationTest
48
48
  end
49
49
 
50
50
  def test_routing_posts_links_tags_update_acts_as_set
51
- assert_routing({path: '/posts/1/links/tags', method: :put},
51
+ assert_routing({path: '/posts/1/links/tags', method: :patch},
52
52
  {controller: 'posts', action: 'update_association', post_id: '1', association: 'tags'})
53
53
  end
54
54
 
55
- def test_routing_authors_show
56
- assert_routing({path: '/authors/1', method: :get},
57
- {action: 'show', controller: 'authors', id: '1'})
58
- end
59
-
60
- def test_routing_author_links_posts_create_not_acts_as_set
61
- assert_routing({path: '/authors/1/links/posts', method: :post},
62
- {controller: 'authors', action: 'create_association', author_id: '1', association: 'posts'})
63
- end
64
-
65
- # ToDo: Test that non acts as set has_many association update route is not created
66
- # def test_routing_author_links_posts_update_not_acts_as_set
67
- # refute_routing({ path: '/authors/1/links/posts', method: :put },
68
- # { controller: 'authors', action: 'update_association', author_id: '1', association: 'posts' })
69
- # end
70
-
71
55
  # V1
72
56
  def test_routing_v1_posts_show
73
57
  assert_routing({path: '/api/v1/posts/1', method: :get},
@@ -85,11 +69,6 @@ class RoutesTest < ActionDispatch::IntegrationTest
85
69
  end
86
70
 
87
71
  # V2
88
- def test_routing_v2_posts_show
89
- assert_routing({path: '/api/v2/authors/1', method: :get},
90
- {action: 'show', controller: 'api/v2/authors', id: '1'})
91
- end
92
-
93
72
  def test_routing_v2_posts_links_author_show
94
73
  assert_routing({path: '/api/v2/posts/1/links/author', method: :get},
95
74
  {controller: 'api/v2/posts', action: 'show_association', post_id: '1', association: 'author'})
@@ -144,6 +123,16 @@ class RoutesTest < ActionDispatch::IntegrationTest
144
123
  {controller: 'api/v5/expense_entries', action: 'show_association', expense_entry_id: '1', association: 'iso_currency'})
145
124
  end
146
125
 
126
+ def test_routing_authors_show
127
+ assert_routing({path: '/api/v5/authors/1', method: :get},
128
+ {action: 'show', controller: 'api/v5/authors', id: '1'})
129
+ end
130
+
131
+ def test_routing_author_links_posts_create_not_acts_as_set
132
+ assert_routing({path: '/api/v5/authors/1/links/posts', method: :post},
133
+ {controller: 'api/v5/authors', action: 'create_association', author_id: '1', association: 'posts'})
134
+ end
135
+
147
136
  #primary_key
148
137
  def test_routing_primary_key_jsonapi_resources
149
138
  assert_routing({path: '/iso_currencies/USD', method: :get},
data/test/test_helper.rb CHANGED
@@ -1,20 +1,18 @@
1
1
  require 'simplecov'
2
2
 
3
- # To run tests with coverage
3
+ # To run tests with coverage:
4
4
  # COVERAGE=true rake test
5
+ # To Switch rails versions and run a particular test order:
6
+ # export RAILS_VERSION=4.2; bundle update rails; bundle exec rake TESTOPTS="--seed=39333" test
7
+
5
8
  if ENV['COVERAGE']
6
9
  SimpleCov.start do
7
10
  end
8
11
  end
9
12
 
10
- require 'minitest/autorun'
11
- require 'minitest/spec'
12
13
  require 'rails/all'
13
-
14
- require 'jsonapi/routing_ext'
15
- require 'jsonapi/configuration'
16
- require 'jsonapi/formatter'
17
- require 'jsonapi/mime_types'
14
+ require 'rails/test_help'
15
+ require 'jsonapi-resources'
18
16
 
19
17
  require File.expand_path('../helpers/value_matchers', __FILE__)
20
18
  require File.expand_path('../helpers/hash_helpers', __FILE__)
@@ -26,26 +24,45 @@ JSONAPI.configure do |config|
26
24
  config.json_key_format = :camelized_key
27
25
  end
28
26
 
27
+ puts "Testing With RAILS VERSION #{Rails.version}"
28
+
29
29
  class TestApp < Rails::Application
30
30
  config.eager_load = false
31
31
  config.root = File.dirname(__FILE__)
32
32
  config.session_store :cookie_store, key: 'session'
33
33
  config.secret_key_base = 'secret'
34
34
 
35
- ActiveSupport::JSON::Encoding.encode_big_decimal_as_string = false
36
-
37
35
  #Raise errors on unsupported parameters
38
36
  config.action_controller.action_on_unpermitted_parameters = :raise
39
37
 
40
38
  config.active_record.schema_format = :none
39
+ config.active_support.test_order = :random
40
+
41
+ # Turn off millisecond precision to maintain Rails 4.0 and 4.1 compatibility in test results
42
+ ActiveSupport::JSON::Encoding.time_precision = 0 if Rails::VERSION::MAJOR >= 4 && Rails::VERSION::MINOR >= 1
43
+ end
44
+
45
+ # Patch RAILS 4.0 to not use millisecond precision
46
+ if Rails::VERSION::MAJOR >= 4 && Rails::VERSION::MINOR < 1
47
+ module ActiveSupport
48
+ class TimeWithZone
49
+ def as_json(options = nil)
50
+ if ActiveSupport::JSON::Encoding.use_standard_json_time_format
51
+ xmlschema
52
+ else
53
+ %(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
54
+ end
55
+ end
56
+ end
57
+ end
41
58
  end
42
59
 
43
60
  TestApp.initialize!
44
61
 
45
62
  require File.expand_path('../fixtures/active_record', __FILE__)
63
+
46
64
  JSONAPI.configuration.route_format = :underscored_route
47
65
  TestApp.routes.draw do
48
- jsonapi_resources :authors
49
66
  jsonapi_resources :people
50
67
  jsonapi_resources :comments
51
68
  jsonapi_resources :tags
@@ -62,7 +79,6 @@ TestApp.routes.draw do
62
79
 
63
80
  namespace :api do
64
81
  namespace :v1 do
65
- jsonapi_resources :authors
66
82
  jsonapi_resources :people
67
83
  jsonapi_resources :comments
68
84
  jsonapi_resources :tags
@@ -80,9 +96,6 @@ TestApp.routes.draw do
80
96
 
81
97
  JSONAPI.configuration.route_format = :underscored_route
82
98
  namespace :v2 do
83
- jsonapi_resources :authors do
84
- end
85
-
86
99
  jsonapi_resources :posts do
87
100
  jsonapi_link :author, except: [:destroy]
88
101
  end
@@ -123,6 +136,7 @@ TestApp.routes.draw do
123
136
  jsonapi_resources :posts do
124
137
  end
125
138
 
139
+ jsonapi_resources :authors
126
140
  jsonapi_resources :expense_entries
127
141
  jsonapi_resources :iso_currencies
128
142
 
@@ -133,18 +147,28 @@ TestApp.routes.draw do
133
147
  end
134
148
  end
135
149
 
136
- class MiniTest::Unit::TestCase
150
+ # Ensure backward compatibility with Minitest 4
151
+ Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)
152
+
153
+ class Minitest::Test
137
154
  include Helpers::HashHelpers
138
155
  include Helpers::ValueMatchers
139
156
  include Helpers::FunctionalHelpers
140
157
  end
141
158
 
142
159
  class ActiveSupport::TestCase
160
+ self.fixture_path = "#{Rails.root}/fixtures"
161
+ fixtures :all
143
162
  setup do
144
163
  @routes = TestApp.routes
145
164
  end
146
165
  end
147
166
 
167
+ class ActionDispatch::IntegrationTest
168
+ self.fixture_path = "#{Rails.root}/fixtures"
169
+ fixtures :all
170
+ end
171
+
148
172
  class UpperCamelizedKeyFormatter < JSONAPI::KeyFormatter
149
173
  class << self
150
174
  def format(key)
@@ -152,7 +176,7 @@ class UpperCamelizedKeyFormatter < JSONAPI::KeyFormatter
152
176
  end
153
177
 
154
178
  def unformat(formatted_key)
155
- formatted_key.to_s.underscore.to_sym
179
+ formatted_key.to_s.underscore
156
180
  end
157
181
  end
158
182
  end
@@ -0,0 +1,152 @@
1
+ require File.expand_path('../../../test_helper', __FILE__)
2
+
3
+ class JSONAPIRequestTest < ActiveSupport::TestCase
4
+ def test_parse_includes_underscored
5
+ params = ActionController::Parameters.new(
6
+ {
7
+ controller: 'expense_entries',
8
+ action: 'index',
9
+ include: 'iso_currency'
10
+ }
11
+ )
12
+
13
+ request = JSONAPI::Request.new(
14
+ params,
15
+ {
16
+ context: nil,
17
+ key_formatter: JSONAPI::Formatter.formatter_for(:underscored_key)
18
+ }
19
+ )
20
+
21
+ assert request.errors.empty?
22
+ end
23
+
24
+ def test_parse_dasherized_with_dasherized_include
25
+ params = ActionController::Parameters.new(
26
+ {
27
+ controller: 'expense_entries',
28
+ action: 'index',
29
+ include: 'iso-currency'
30
+ }
31
+ )
32
+
33
+ request = JSONAPI::Request.new(
34
+ params,
35
+ {
36
+ context: nil,
37
+ key_formatter: JSONAPI::Formatter.formatter_for(:dasherized_key)
38
+ }
39
+ )
40
+
41
+ assert request.errors.empty?
42
+ end
43
+
44
+ def test_parse_dasherized_with_underscored_include
45
+ params = ActionController::Parameters.new(
46
+ {
47
+ controller: 'expense_entries',
48
+ action: 'index',
49
+ include: 'iso_currency'
50
+ }
51
+ )
52
+
53
+ request = JSONAPI::Request.new(
54
+ params,
55
+ {
56
+ context: nil,
57
+ key_formatter: JSONAPI::Formatter.formatter_for(:dasherized_key)
58
+ }
59
+ )
60
+
61
+ refute request.errors.empty?
62
+ assert_equal 'iso_currency is not a valid association of expense-entries', request.errors[0].detail
63
+ end
64
+
65
+ def test_parse_fields_underscored
66
+ params = ActionController::Parameters.new(
67
+ {
68
+ controller: 'expense_entries',
69
+ action: 'index',
70
+ fields: {expense_entries: 'iso_currency'}
71
+ }
72
+ )
73
+
74
+ request = JSONAPI::Request.new(
75
+ params,
76
+ {
77
+ context: nil,
78
+ key_formatter: JSONAPI::Formatter.formatter_for(:underscored_key)
79
+ }
80
+ )
81
+
82
+ assert request.errors.empty?
83
+ end
84
+
85
+ def test_parse_dasherized_with_dasherized_fields
86
+ params = ActionController::Parameters.new(
87
+ {
88
+ controller: 'expense_entries',
89
+ action: 'index',
90
+ fields: {
91
+ 'expense-entries' => 'iso-currency'
92
+ }
93
+ }
94
+ )
95
+
96
+ request = JSONAPI::Request.new(
97
+ params,
98
+ {
99
+ context: nil,
100
+ key_formatter: JSONAPI::Formatter.formatter_for(:dasherized_key)
101
+ }
102
+ )
103
+
104
+ assert request.errors.empty?
105
+ end
106
+
107
+ def test_parse_dasherized_with_underscored_fields
108
+ params = ActionController::Parameters.new(
109
+ {
110
+ controller: 'expense_entries',
111
+ action: 'index',
112
+ fields: {
113
+ 'expense-entries' => 'iso_currency'
114
+ }
115
+ }
116
+ )
117
+
118
+ request = JSONAPI::Request.new(
119
+ params,
120
+ {
121
+ context: nil,
122
+ key_formatter: JSONAPI::Formatter.formatter_for(:dasherized_key)
123
+ }
124
+ )
125
+
126
+ refute request.errors.empty?
127
+ assert_equal 'iso_currency is not a valid field for expense-entries.', request.errors[0].detail
128
+ end
129
+
130
+ def test_parse_dasherized_with_underscored_resource
131
+ params = ActionController::Parameters.new(
132
+ {
133
+ controller: 'expense_entries',
134
+ action: 'index',
135
+ fields: {
136
+ 'expense_entries' => 'iso-currency'
137
+ }
138
+ }
139
+ )
140
+
141
+ request = JSONAPI::Request.new(
142
+ params,
143
+ {
144
+ context: nil,
145
+ key_formatter: JSONAPI::Formatter.formatter_for(:dasherized_key)
146
+ }
147
+ )
148
+
149
+ refute request.errors.empty?
150
+ assert_equal 'expense_entries is not a valid resource.', request.errors[0].detail
151
+ end
152
+ end
@@ -1,5 +1,4 @@
1
1
  require File.expand_path('../../../test_helper', __FILE__)
2
- require File.expand_path('../../../fixtures/active_record', __FILE__)
3
2
 
4
3
  require 'jsonapi/operation'
5
4
  require 'jsonapi/operation_result'
@@ -55,7 +54,7 @@ class TestOperationsProcessor < JSONAPI::OperationsProcessor
55
54
  end
56
55
  end
57
56
 
58
- class OperationsProcessorTest < MiniTest::Unit::TestCase
57
+ class OperationsProcessorTest < Minitest::Test
59
58
  def setup
60
59
  betax = Planet.find(5)
61
60
  betay = Planet.find(6)
@@ -129,6 +128,18 @@ class OperationsProcessorTest < MiniTest::Unit::TestCase
129
128
  saturn.reload
130
129
  assert_equal(saturn.planet_type_id, gas_giant.id)
131
130
 
131
+ # Remove link
132
+ operations = [
133
+ JSONAPI::ReplaceHasOneAssociationOperation.new(PlanetResource, saturn.id, :planet_type, nil)
134
+ ]
135
+
136
+ request = JSONAPI::Request.new
137
+ request.operations = operations
138
+
139
+ results = op.process(request)
140
+ saturn.reload
141
+ assert_equal(saturn.planet_type_id, nil)
142
+
132
143
  # Reset
133
144
  operations = [
134
145
  JSONAPI::ReplaceHasOneAssociationOperation.new(PlanetResource, saturn.id, :planet_type, 5)
@@ -1,5 +1,4 @@
1
1
  require File.expand_path('../../../test_helper', __FILE__)
2
- require File.expand_path('../../../fixtures/active_record', __FILE__)
3
2
 
4
3
  class ArticleResource < JSONAPI::Resource
5
4
  model_name 'Post'
@@ -18,7 +17,29 @@ class CatResource < JSONAPI::Resource
18
17
  has_one :father, class_name: 'Cat'
19
18
  end
20
19
 
21
- class ResourceTest < MiniTest::Unit::TestCase
20
+ class PersonWithCustomRecordsForResource < PersonResource
21
+ def records_for(association_name, context)
22
+ :records_for
23
+ end
24
+ end
25
+
26
+ class PersonWithCustomRecordsForRelationshipsResource < PersonResource
27
+ def records_for_posts(options = {})
28
+ :records_for_posts
29
+ end
30
+ def record_for_preferences(options = {})
31
+ :record_for_preferences
32
+ end
33
+ end
34
+
35
+ class PersonWithCustomRecordsForErrorResource < PersonResource
36
+ class AuthorizationError < StandardError; end
37
+ def records_for(association_name, context)
38
+ raise AuthorizationError
39
+ end
40
+ end
41
+
42
+ class ResourceTest < ActiveSupport::TestCase
22
43
  def setup
23
44
  @post = Post.first
24
45
  end
@@ -41,7 +62,7 @@ class ResourceTest < MiniTest::Unit::TestCase
41
62
  assert_equal(attrs.keys.size, 3)
42
63
  end
43
64
 
44
- def test_class_assosications
65
+ def test_class_associations
45
66
  associations = CatResource._associations
46
67
  assert_kind_of(Hash, associations)
47
68
  assert_equal(associations.size, 2)
@@ -55,25 +76,59 @@ class ResourceTest < MiniTest::Unit::TestCase
55
76
  refute(posts.include?(Post.find(3)))
56
77
  end
57
78
 
58
- def test_find_by_key_with_customized_base_records
79
+ def test_records_for
59
80
  author = Person.find(1)
81
+ preferences = Preferences.first
82
+ refute(preferences == nil)
83
+ author.update! preferences: preferences
84
+ author_resource = PersonResource.new(author)
85
+ assert_equal(author_resource.preferences.model, preferences)
86
+
87
+ author_resource = PersonWithCustomRecordsForResource.new(author)
88
+ assert_equal(author_resource.preferences.model, :records_for)
89
+
90
+ author_resource = PersonWithCustomRecordsForErrorResource.new(author)
91
+ assert_raises PersonWithCustomRecordsForErrorResource::AuthorizationError do
92
+ author_resource.posts
93
+ end
94
+ end
60
95
 
61
- post = ArticleResource.find_by_key(1, context: author).model
62
- assert_equal(post, Post.find(1))
96
+ def test_records_for_meta_method_for_has_one
97
+ author = Person.find(1)
98
+ author.update! preferences: Preferences.first
99
+ author_resource = PersonWithCustomRecordsForRelationshipsResource.new(author)
100
+ assert_equal(author_resource.record_for_preferences, :record_for_preferences)
101
+ end
63
102
 
64
- assert_raises JSONAPI::Exceptions::RecordNotFound do
65
- ArticleResource.find_by_key(3, context: author).model
66
- end
103
+ def test_records_for_meta_method_for_has_one_calling_records_for
104
+ author = Person.find(1)
105
+ author.update! preferences: Preferences.first
106
+ author_resource = PersonWithCustomRecordsForResource.new(author)
107
+ assert_equal(author_resource.record_for_preferences, :records_for)
67
108
  end
68
109
 
69
- def test_find_by_keys_with_customized_base_records
110
+ def test_associated_records_meta_method_for_has_many
70
111
  author = Person.find(1)
112
+ author.posts << Post.find(1)
113
+ author_resource = PersonWithCustomRecordsForRelationshipsResource.new(author)
114
+ assert_equal(author_resource.records_for_posts, :records_for_posts)
115
+ end
71
116
 
72
- posts = ArticleResource.find_by_keys([1, 2], context: author)
73
- assert_equal(posts.length, 2)
117
+ def test_associated_records_meta_method_for_has_many_calling_records_for
118
+ author = Person.find(1)
119
+ author.posts << Post.find(1)
120
+ author_resource = PersonWithCustomRecordsForResource.new(author)
121
+ assert_equal(author_resource.records_for_posts, :records_for)
122
+ end
123
+
124
+ def test_find_by_key_with_customized_base_records
125
+ author = Person.find(1)
126
+
127
+ post = ArticleResource.find_by_key(1, context: author).model
128
+ assert_equal(post, Post.find(1))
74
129
 
75
130
  assert_raises JSONAPI::Exceptions::RecordNotFound do
76
- ArticleResource.find_by_keys([1, 3], context: author).model
131
+ ArticleResource.find_by_key(3, context: author).model
77
132
  end
78
133
  end
79
134