jsonapi-resources 0.2.0 → 0.3.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
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