jsonapi-resources 0.0.4 → 0.0.5

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.
@@ -40,17 +40,20 @@ ActiveRecord::Schema.define do
40
40
  create_table :posts_tags, force: true do |t|
41
41
  t.references :post, :tag, index: true
42
42
  end
43
+ add_index :posts_tags, [:post_id, :tag_id], unique: true
43
44
 
44
45
  create_table :comments_tags, force: true do |t|
45
46
  t.references :comment, :tag, index: true
46
47
  end
47
48
 
48
- create_table :currencies, id: false, force: true do |t|
49
+ create_table :iso_currencies, id: false, force: true do |t|
49
50
  t.string :code, limit: 3, null: false
50
51
  t.string :name
52
+ t.string :country_name
53
+ t.string :minor_unit
51
54
  t.timestamps
52
55
  end
53
- add_index :currencies, :code, unique: true
56
+ add_index :iso_currencies, :code, unique: true
54
57
 
55
58
  create_table :expense_entries, force: true do |t|
56
59
  t.string :currency_code, limit: 3, null: false
@@ -65,6 +68,11 @@ ActiveRecord::Schema.define do
65
68
  t.integer :planet_type_id
66
69
  end
67
70
 
71
+ create_table :planets_tags, force: true do |t|
72
+ t.references :planet, :tag, index: true
73
+ end
74
+ add_index :planets_tags, [:planet_id, :tag_id], unique: true
75
+
68
76
  create_table :planet_types, force: true do |t|
69
77
  t.string :name
70
78
  end
@@ -109,24 +117,27 @@ end
109
117
 
110
118
  class Tag < ActiveRecord::Base
111
119
  has_and_belongs_to_many :posts, join_table: :posts_tags
120
+ has_and_belongs_to_many :planets, join_table: :planets_tags
112
121
  end
113
122
 
114
123
  class Section < ActiveRecord::Base
115
124
  end
116
125
 
117
- class Currency < ActiveRecord::Base
126
+ class IsoCurrency < ActiveRecord::Base
118
127
  self.primary_key = :code
119
- has_many :expense_entries, foreign_key: 'currency_code'
128
+ # has_many :expense_entries, foreign_key: 'currency_code'
120
129
  end
121
130
 
122
131
  class ExpenseEntry < ActiveRecord::Base
123
132
  belongs_to :employee, class_name: 'Person', foreign_key: 'employee_id'
124
- belongs_to :currency, class_name: 'Currency', foreign_key: 'currency_code'
133
+ belongs_to :iso_currency, foreign_key: 'currency_code'
125
134
  end
126
135
 
127
136
  class Planet < ActiveRecord::Base
128
137
  has_many :moons
129
138
  has_one :planet_type
139
+
140
+ has_and_belongs_to_many :tags, join_table: :planets_tags
130
141
  end
131
142
 
132
143
  class PlanetType < ActiveRecord::Base
@@ -204,7 +215,7 @@ end
204
215
  class TagsController < JSONAPI::ResourceController
205
216
  end
206
217
 
207
- class CurrenciesController < JSONAPI::ResourceController
218
+ class IsoCurrenciesController < JSONAPI::ResourceController
208
219
  end
209
220
 
210
221
  class ExpenseEntriesController < JSONAPI::ResourceController
@@ -228,7 +239,7 @@ module Api
228
239
  class TagsController < JSONAPI::ResourceController
229
240
  end
230
241
 
231
- class CurrenciesController < JSONAPI::ResourceController
242
+ class IsoCurrenciesController < JSONAPI::ResourceController
232
243
  end
233
244
 
234
245
  class ExpenseEntriesController < JSONAPI::ResourceController
@@ -236,6 +247,15 @@ module Api
236
247
 
237
248
  class BreedsController < JSONAPI::ResourceController
238
249
  end
250
+
251
+ class PlanetsController < JSONAPI::ResourceController
252
+ end
253
+
254
+ class PlanetTypesController < JSONAPI::ResourceController
255
+ end
256
+
257
+ class MoonsController < JSONAPI::ResourceController
258
+ end
239
259
  end
240
260
 
241
261
  module V2
@@ -260,7 +280,9 @@ end
260
280
 
261
281
  ### RESOURCES
262
282
  class PersonResource < JSONAPI::Resource
263
- attributes :id, :name, :email, :date_joined
283
+ attributes :id, :name, :email
284
+ attribute :date_joined, format: :date_with_timezone
285
+
264
286
  has_many :comments
265
287
  has_many :posts
266
288
 
@@ -317,6 +339,8 @@ class TagResource < JSONAPI::Resource
317
339
  attributes :id, :name
318
340
 
319
341
  has_many :posts
342
+ # Not including the planets association so they don't get output
343
+ #has_many :planets
320
344
  end
321
345
 
322
346
  class SectionResource < JSONAPI::Resource
@@ -340,12 +364,12 @@ class PostResource < JSONAPI::Resource
340
364
  filters :title, :author, :tags, :comments
341
365
  filter :id
342
366
 
343
- def self.updateable(keys, context = nil)
344
- super(keys - [:author, :subject])
367
+ def self.updateable_fields(context)
368
+ super(context) - [:author, :subject]
345
369
  end
346
370
 
347
- def self.createable(keys, context = nil)
348
- super(keys - [:subject])
371
+ def self.createable_fields(context)
372
+ super(context) - [:subject]
349
373
  end
350
374
 
351
375
  def self.verify_custom_filter(filter, values, context = nil)
@@ -373,24 +397,24 @@ class PostResource < JSONAPI::Resource
373
397
  end
374
398
  end
375
399
 
376
- class CurrencyResource < JSONAPI::Resource
400
+ class IsoCurrencyResource < JSONAPI::Resource
377
401
  key :code
378
- attributes :code, :name
402
+ attributes :code, :name, :country_name, :minor_unit
379
403
 
380
404
  routing_options :param => :code
381
-
382
- has_many :expense_entries
383
405
  end
384
406
 
385
407
  class ExpenseEntryResource < JSONAPI::Resource
386
- attributes :id, :cost, :transaction_date
408
+ attributes :id, :cost
409
+ attribute :transaction_date, format: :date
387
410
 
388
- has_one :currency, class_name: 'Currency', key: 'currency_code'
389
- has_one :employee
411
+ has_one :iso_currency, key: 'currency_code', primary_key: 'code'
412
+ has_one :employee, class_name: 'Person'
390
413
  end
391
414
 
392
415
  class BreedResource < JSONAPI::Resource
393
- attributes :id, :name
416
+ attribute :id, format_misspelled: :does_not_exist
417
+ attribute :name, format: :title
394
418
 
395
419
  def self.find(attrs, context = nil)
396
420
  breeds = []
@@ -412,6 +436,14 @@ class PlanetResource < JSONAPI::Resource
412
436
 
413
437
  has_many :moons
414
438
  has_one :planet_type
439
+
440
+ has_many :tags, acts_as_set: true
441
+ end
442
+
443
+ class PropertyResource < JSONAPI::Resource
444
+ attributes :id, :name
445
+
446
+ has_many :planets
415
447
  end
416
448
 
417
449
  class PlanetTypeResource < JSONAPI::Resource
@@ -542,25 +574,27 @@ Post.create(title: 'JR How To',
542
574
  post.tags.concat jr_tag
543
575
  end
544
576
 
545
- Currency.create(code: 'USD', name: 'United States Dollar')
546
- Currency.create(code: 'EUR', name: 'Euro Member Countries')
577
+ IsoCurrency.create(code: 'USD', name: 'United States Dollar', country_name: 'United States', minor_unit: 'cent')
578
+ IsoCurrency.create(code: 'EUR', name: 'Euro Member Countries', country_name: 'Euro Member Countries', minor_unit: 'cent')
547
579
 
548
580
  ExpenseEntry.create(currency_code: 'USD',
549
581
  employee_id: c.id,
550
582
  cost: '12.05',
551
- transaction_date: DateTime.parse('2014-04-15 12:13:14 UTC +00:00'))
583
+ transaction_date: Date.parse('2014-04-15'))
552
584
 
553
585
  ExpenseEntry.create(currency_code: 'USD',
554
586
  employee_id: c.id,
555
587
  cost: '12.06',
556
- transaction_date: DateTime.parse('2014-04-15 12:13:15 UTC +00:00'))
588
+ transaction_date: Date.parse('2014-04-15'))
557
589
 
590
+ # id:11
558
591
  Post.create(title: 'Tagged up post 1',
559
592
  body: 'AAAA',
560
593
  author_id: d.id,
561
594
  tag_ids: [6,7,8,9]
562
595
  )
563
596
 
597
+ # id:12
564
598
  Post.create(title: 'Tagged up post 2',
565
599
  body: 'BBBB',
566
600
  author_id: d.id,
@@ -583,3 +617,5 @@ jupiter = Planet.create(name: 'Jupiter', description: 'A gas giant.', planet_typ
583
617
  betax = Planet.create(name: 'Beta X', description: 'Newly discovered Planet X', planet_type_id: unknown.id)
584
618
  betay = Planet.create(name: 'Beta X', description: 'Newly discovered Planet Y', planet_type_id: unknown.id)
585
619
  betaz = Planet.create(name: 'Beta X', description: 'Newly discovered Planet Z', planet_type_id: unknown.id)
620
+ betaw = Planet.create(name: 'Beta W', description: 'Newly discovered Planet W')
621
+
@@ -9,23 +9,19 @@ class RequestTest < ActionDispatch::IntegrationTest
9
9
  end
10
10
 
11
11
  def test_put_single
12
- put '/posts/3', {"posts" => {"id" => "3", "title" => "A great new Post", "links" => { "tags" => [3,4] }}}
12
+ put '/posts/3',
13
+ {
14
+ 'posts' => {
15
+ 'id' => '3',
16
+ 'title' => 'A great new Post',
17
+ 'links' => {
18
+ 'tags' => [3, 4]
19
+ }
20
+ }
21
+ }
13
22
  assert_equal 200, status
14
23
  end
15
24
 
16
- # def test_put_links
17
- # put '/posts/3/links/tags', {"tags" => [1,4] }
18
- # assert_equal 200, status
19
- # end
20
-
21
- # def test_patch_create
22
- # patch '/posts',
23
- # {"op" => "add",
24
- # "path" => "/-",
25
- # "value" => {"title" => "Another great new Post", "body" => "saasd", "links" => { "author" => 3 }}}
26
- # assert_equal 200, status
27
- # end
28
-
29
25
  def test_destroy_single
30
26
  delete '/posts/7'
31
27
  assert_equal 204, status
@@ -36,4 +32,3 @@ class RequestTest < ActionDispatch::IntegrationTest
36
32
  assert_equal 204, status
37
33
  end
38
34
  end
39
-
@@ -3,83 +3,131 @@ require File.expand_path('../../../test_helper', __FILE__)
3
3
  class RoutesTest < ActionDispatch::IntegrationTest
4
4
 
5
5
  def test_routing_post
6
- assert_routing({ path: 'posts', method: :post }, { controller: 'posts', action: 'create' })
6
+ assert_routing({path: 'posts', method: :post},
7
+ {controller: 'posts', action: 'create'})
7
8
  end
8
9
 
9
10
  def test_routing_put
10
- assert_routing({ path: '/posts/1', method: :put }, { controller: 'posts', action: 'update', id: '1' })
11
+ assert_routing({path: '/posts/1', method: :put},
12
+ {controller: 'posts', action: 'update', id: '1'})
11
13
  end
12
14
 
13
15
  def test_routing_posts_show
14
- assert_routing({ path: '/posts/1', method: :get }, {action: 'show', controller: 'posts', id: '1'})
16
+ assert_routing({path: '/posts/1', method: :get},
17
+ {action: 'show', controller: 'posts', id: '1'})
15
18
  end
16
19
 
17
20
  def test_routing_posts_links_author_show
18
- assert_routing({ path: '/posts/1/links/author', method: :get }, { controller: 'posts', action: 'show_association', post_id: '1', association: 'author' })
21
+ assert_routing({path: '/posts/1/links/author', method: :get},
22
+ {controller: 'posts', action: 'show_association', post_id: '1', association: 'author'})
19
23
  end
20
24
 
21
25
  def test_routing_posts_links_author_destroy
22
- assert_routing({ path: '/posts/1/links/author', method: :delete }, { controller: 'posts', action: 'destroy_association', post_id: '1', association: 'author' })
26
+ assert_routing({path: '/posts/1/links/author', method: :delete},
27
+ {controller: 'posts', action: 'destroy_association', post_id: '1', association: 'author'})
23
28
  end
24
29
 
25
30
  def test_routing_posts_links_author_create
26
- assert_routing({ path: '/posts/1/links/author', method: :post }, { controller: 'posts', action: 'create_association', post_id: '1', association: 'author' })
31
+ assert_routing({path: '/posts/1/links/author', method: :post},
32
+ {controller: 'posts', action: 'create_association', post_id: '1', association: 'author'})
33
+ end
34
+
35
+ def test_routing_posts_links_author_update
36
+ assert_routing({path: '/posts/1/links/author', method: :put},
37
+ {controller: 'posts', action: 'update_association', post_id: '1', association: 'author'})
27
38
  end
28
39
 
29
40
  def test_routing_posts_links_tags_show
30
- assert_routing({ path: '/posts/1/links/tags', method: :get }, { controller: 'posts', action: 'show_association', post_id: '1', association: 'tags' })
41
+ assert_routing({path: '/posts/1/links/tags', method: :get},
42
+ {controller: 'posts', action: 'show_association', post_id: '1', association: 'tags'})
31
43
  end
32
44
 
33
45
  def test_routing_posts_links_tags_destroy
34
- assert_routing({ path: '/posts/1/links/tags/1,2', method: :delete }, { controller: 'posts', action: 'destroy_association', post_id: '1', keys: '1,2', association: 'tags' })
46
+ assert_routing({path: '/posts/1/links/tags/1,2', method: :delete},
47
+ {controller: 'posts', action: 'destroy_association', post_id: '1', keys: '1,2', association: 'tags'})
48
+ end
49
+
50
+ def test_routing_posts_links_tags_create
51
+ assert_routing({path: '/posts/1/links/tags', method: :post},
52
+ {controller: 'posts', action: 'create_association', post_id: '1', association: 'tags'})
53
+ end
54
+
55
+ def test_routing_posts_links_tags_update_acts_as_set
56
+ assert_routing({path: '/posts/1/links/tags', method: :put},
57
+ {controller: 'posts', action: 'update_association', post_id: '1', association: 'tags'})
35
58
  end
36
59
 
37
- def test_routing_posts_links_tags_update
38
- assert_routing({ path: '/posts/1/links/tags', method: :post }, { controller: 'posts', action: 'create_association', post_id: '1', association: 'tags' })
60
+ def test_routing_authors_show
61
+ assert_routing({path: '/authors/1', method: :get},
62
+ {action: 'show', controller: 'authors', id: '1'})
39
63
  end
40
64
 
65
+ def test_routing_author_links_posts_create_not_acts_as_set
66
+ assert_routing({path: '/authors/1/links/posts', method: :post},
67
+ {controller: 'authors', action: 'create_association', author_id: '1', association: 'posts'})
68
+ end
69
+
70
+ # ToDo: Test that non acts as set has_many association update route is not created
71
+ # def test_routing_author_links_posts_update_not_acts_as_set
72
+ # refute_routing({ path: '/authors/1/links/posts', method: :put },
73
+ # { controller: 'authors', action: 'update_association', author_id: '1', association: 'posts' })
74
+ # end
75
+
41
76
  # V1
42
77
  def test_routing_v1_posts_show
43
- assert_routing({ path: '/api/v1/posts/1', method: :get }, {action: 'show', controller: 'api/v1/posts', id: '1'})
78
+ assert_routing({path: '/api/v1/posts/1', method: :get},
79
+ {action: 'show', controller: 'api/v1/posts', id: '1'})
44
80
  end
45
81
 
46
82
  def test_routing_v1_posts_delete
47
- assert_routing({ path: '/api/v1/posts/1', method: :delete }, {action: 'destroy', controller: 'api/v1/posts', id: '1'})
83
+ assert_routing({path: '/api/v1/posts/1', method: :delete},
84
+ {action: 'destroy', controller: 'api/v1/posts', id: '1'})
48
85
  end
49
86
 
50
87
  def test_routing_v1_posts_links_author_show
51
- assert_routing({ path: '/api/v1/posts/1/links/author', method: :get }, { controller: 'api/v1/posts', action: 'show_association', post_id: '1', association: 'author' })
88
+ assert_routing({path: '/api/v1/posts/1/links/author', method: :get},
89
+ {controller: 'api/v1/posts', action: 'show_association', post_id: '1', association: 'author'})
52
90
  end
53
91
 
54
92
  # V2
55
93
  def test_routing_v2_posts_show
56
- assert_routing({ path: '/api/v2/authors/1', method: :get }, {action: 'show', controller: 'api/v2/authors', id: '1'})
94
+ assert_routing({path: '/api/v2/authors/1', method: :get},
95
+ {action: 'show', controller: 'api/v2/authors', id: '1'})
57
96
  end
58
97
 
59
98
  def test_routing_v2_posts_links_author_show
60
- assert_routing({ path: '/api/v2/posts/1/links/author', method: :get }, { controller: 'api/v2/posts', action: 'show_association', post_id: '1', association: 'author' })
99
+ assert_routing({path: '/api/v2/posts/1/links/author', method: :get},
100
+ {controller: 'api/v2/posts', action: 'show_association', post_id: '1', association: 'author'})
61
101
  end
62
102
 
63
103
  def test_routing_v2_preferences_show
64
- assert_routing({ path: '/api/v2/preferences', method: :get }, {action: 'show', controller: 'api/v2/preferences'})
104
+ assert_routing({path: '/api/v2/preferences', method: :get},
105
+ {action: 'show', controller: 'api/v2/preferences'})
65
106
  end
66
107
 
67
108
  # V3
68
109
  def test_routing_v3_posts_show
69
- assert_routing({ path: '/api/v3/posts/1', method: :get }, {action: 'show', controller: 'api/v3/posts', id: '1'})
110
+ assert_routing({path: '/api/v3/posts/1', method: :get},
111
+ {action: 'show', controller: 'api/v3/posts', id: '1'})
70
112
  end
71
113
 
72
114
  # ToDo: Refute routing
73
115
  # def test_routing_v3_posts_delete
74
- # assert_routing({ path: '/api/v3/posts/1', method: :delete }, {action: 'destroy', controller: 'api/v3/posts', id: '1'})
116
+ # assert_routing({ path: '/api/v3/posts/1', method: :delete },
117
+ # {action: 'destroy', controller: 'api/v3/posts', id: '1'})
75
118
  # end
76
119
 
77
120
  # def test_routing_posts_links_author_except_destroy
78
- # assert_routing({ path: '/api/v3/posts/1/links/author', method: :delete }, { controller: 'api/v3/posts', action: 'destroy_association', post_id: '1', association: 'author' })
121
+ # assert_routing({ path: '/api/v3/posts/1/links/author', method: :delete },
122
+ # { controller: 'api/v3/posts', action: 'destroy_association', post_id: '1', association: 'author' })
79
123
  # end
80
124
  #
81
125
  # def test_routing_posts_links_tags_only_create_show
82
- # assert_routing({ path: '/api/v3/posts/1/links/tags/1,2', method: :delete }, { controller: 'api/v3/posts', action: 'destroy_association', post_id: '1', keys: '1,2', association: 'tags' })
126
+ # assert_routing({ path: '/api/v3/posts/1/links/tags/1,2', method: :delete },
127
+ # { controller: 'api/v3/posts', action: 'destroy_association', post_id: '1', keys: '1,2', association: 'tags' })
83
128
  # end
129
+
130
+ # Test that non acts as set has_many association update route is not created
131
+
84
132
  end
85
133
 
data/test/test_helper.rb CHANGED
@@ -12,6 +12,8 @@ require 'minitest/spec'
12
12
  require 'rails/all'
13
13
 
14
14
  require 'jsonapi/routing_ext'
15
+ require 'jsonapi/configuration'
16
+ require 'jsonapi/formatter'
15
17
 
16
18
  require File.expand_path('../helpers/value_matchers', __FILE__)
17
19
  require File.expand_path('../helpers/hash_helpers', __FILE__)
@@ -19,6 +21,10 @@ require File.expand_path('../helpers/functional_helpers', __FILE__)
19
21
 
20
22
  Rails.env = 'test'
21
23
 
24
+ JSONAPI.configure do |config|
25
+ config.json_key_format = :camelized_key
26
+ end
27
+
22
28
  class TestApp < Rails::Application
23
29
  config.eager_load = false
24
30
  config.root = File.dirname(__FILE__)
@@ -40,7 +46,7 @@ TestApp.routes.draw do
40
46
  jsonapi_resources :tags
41
47
  jsonapi_resources :posts
42
48
  jsonapi_resources :sections
43
- jsonapi_resources :currencies
49
+ jsonapi_resources :iso_currencies
44
50
  jsonapi_resources :expense_entries
45
51
  jsonapi_resources :breeds
46
52
  jsonapi_resources :planets
@@ -57,7 +63,7 @@ TestApp.routes.draw do
57
63
  jsonapi_resources :tags
58
64
  jsonapi_resources :posts
59
65
  jsonapi_resources :sections
60
- jsonapi_resources :currencies
66
+ jsonapi_resources :iso_currencies
61
67
  jsonapi_resources :expense_entries
62
68
  jsonapi_resources :breeds
63
69
  jsonapi_resources :planets
@@ -74,7 +80,7 @@ TestApp.routes.draw do
74
80
 
75
81
  namespace :v3 do
76
82
  jsonapi_resource :preferences do
77
- # Intentionally empty block to skip association urls
83
+ # Intentionally empty block to skip association urls
78
84
  end
79
85
 
80
86
  jsonapi_resources :posts, except: [:destroy] do
@@ -96,3 +102,39 @@ class ActiveSupport::TestCase
96
102
  @routes = TestApp.routes
97
103
  end
98
104
  end
105
+
106
+ class UpperCamelizedKeyFormatter < JSONAPI::KeyFormatter
107
+ class << self
108
+ def format(key)
109
+ super.camelize(:upper)
110
+ end
111
+ end
112
+ end
113
+
114
+ class DateWithTimezoneValueFormatter < JSONAPI::ValueFormatter
115
+ class << self
116
+ def format(raw_value, source, context)
117
+ raw_value.in_time_zone('Eastern Time (US & Canada)').to_s
118
+ end
119
+ end
120
+ end
121
+
122
+ class DateValueFormatter < JSONAPI::ValueFormatter
123
+ class << self
124
+ def format(raw_value, source, context)
125
+ raw_value.strftime('%m/%d/%Y')
126
+ end
127
+ end
128
+ end
129
+
130
+ class TitleValueFormatter < JSONAPI::ValueFormatter
131
+ class << self
132
+ def format(raw_value, source, context)
133
+ super(raw_value, source, context).titlecase
134
+ end
135
+
136
+ def unformat(value, resource_klass, context)
137
+ value.to_s.downcase
138
+ end
139
+ end
140
+ end
@@ -7,6 +7,10 @@ require 'jsonapi/operations_processor'
7
7
 
8
8
  class OperationsProcessorTest < MiniTest::Unit::TestCase
9
9
  def setup
10
+ betax = Planet.find(5)
11
+ betay = Planet.find(6)
12
+ betaz = Planet.find(7)
13
+ unknown = PlanetType.find(5)
10
14
  end
11
15
 
12
16
  def test_create_single_resource
@@ -51,7 +55,7 @@ class OperationsProcessorTest < MiniTest::Unit::TestCase
51
55
  assert_equal(Planet.count, count + 3)
52
56
  end
53
57
 
54
- def test_add_has_one_association
58
+ def test_replace_has_one_association
55
59
  op = JSONAPI::OperationsProcessor.new()
56
60
 
57
61
  saturn = Planet.find(1)
@@ -71,11 +75,13 @@ class OperationsProcessorTest < MiniTest::Unit::TestCase
71
75
 
72
76
  assert_kind_of(Array, results)
73
77
  assert_kind_of(JSONAPI::OperationResult, results[0])
74
- assert_equal(:created, results[0].code)
75
- assert_equal(results[0].resource.object.attributes['planet_type_id'], gas_giant.id)
78
+ assert_equal(:no_content, results[0].code)
79
+
80
+ saturn.reload
81
+ assert_equal(saturn.planet_type_id, gas_giant.id)
76
82
  end
77
83
 
78
- def test_add_has_many_association
84
+ def test_create_has_many_association
79
85
  op = JSONAPI::OperationsProcessor.new()
80
86
 
81
87
  betax = Planet.find(5)
@@ -83,9 +89,12 @@ class OperationsProcessorTest < MiniTest::Unit::TestCase
83
89
  betaz = Planet.find(7)
84
90
  gas_giant = PlanetType.find(1)
85
91
  unknown = PlanetType.find(5)
86
- assert_equal(betax.planet_type_id, unknown.id)
87
- assert_equal(betay.planet_type_id, unknown.id)
88
- assert_equal(betaz.planet_type_id, unknown.id)
92
+ betax.planet_type_id = unknown.id
93
+ betay.planet_type_id = unknown.id
94
+ betaz.planet_type_id = unknown.id
95
+ betax.save!
96
+ betay.save!
97
+ betaz.save!
89
98
 
90
99
  operations = [
91
100
  JSONAPI::CreateHasManyAssociationOperation.new(PlanetTypeResource, gas_giant.id, :planets, [betax.id, betay.id, betaz.id])
@@ -105,6 +114,39 @@ class OperationsProcessorTest < MiniTest::Unit::TestCase
105
114
  assert_equal(betaz.planet_type_id, gas_giant.id)
106
115
  end
107
116
 
117
+ def test_replace_has_many_association
118
+ op = JSONAPI::OperationsProcessor.new()
119
+
120
+ betax = Planet.find(5)
121
+ betay = Planet.find(6)
122
+ betaz = Planet.find(7)
123
+ gas_giant = PlanetType.find(1)
124
+ unknown = PlanetType.find(5)
125
+ betax.planet_type_id = unknown.id
126
+ betay.planet_type_id = unknown.id
127
+ betaz.planet_type_id = unknown.id
128
+ betax.save!
129
+ betay.save!
130
+ betaz.save!
131
+
132
+ operations = [
133
+ JSONAPI::ReplaceHasManyAssociationOperation.new(PlanetTypeResource, gas_giant.id, :planets, [betax.id, betay.id, betaz.id])
134
+ ]
135
+
136
+ request = JSONAPI::Request.new
137
+ request.operations = operations
138
+
139
+ results = op.process(request)
140
+
141
+ betax.reload
142
+ betay.reload
143
+ betaz.reload
144
+
145
+ assert_equal(betax.planet_type_id, gas_giant.id)
146
+ assert_equal(betay.planet_type_id, gas_giant.id)
147
+ assert_equal(betaz.planet_type_id, gas_giant.id)
148
+ end
149
+
108
150
  def test_replace_attributes
109
151
  op = JSONAPI::OperationsProcessor.new()
110
152
 
@@ -33,8 +33,8 @@ class ResourceTest < MiniTest::Unit::TestCase
33
33
 
34
34
  def test_class_attributes
35
35
  attrs = CatResource._attributes
36
- assert_kind_of(Set, attrs)
37
- assert_equal(attrs.size, 3)
36
+ assert_kind_of(Hash, attrs)
37
+ assert_equal(attrs.keys.size, 3)
38
38
  end
39
39
 
40
40
  def test_class_assosications