jsonapi-resources 0.0.1

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.
@@ -0,0 +1,585 @@
1
+ require 'active_record'
2
+ require 'jsonapi/resource_controller'
3
+ require 'jsonapi/resource'
4
+ require 'jsonapi/exceptions'
5
+ require 'rails'
6
+ require 'rails/all'
7
+
8
+ ### DATABASE
9
+ ActiveRecord::Schema.define do
10
+ create_table :people, force: true do |t|
11
+ t.string :name
12
+ t.string :email
13
+ t.datetime :date_joined
14
+ t.timestamps
15
+ end
16
+
17
+ create_table :posts, force: true do |t|
18
+ t.string :title
19
+ t.text :body
20
+ t.integer :author_id
21
+ t.belongs_to :section, index: true
22
+ t.timestamps
23
+ end
24
+
25
+ create_table :comments, force: true do |t|
26
+ t.text :body
27
+ t.belongs_to :post, index: true
28
+ t.integer :author_id
29
+ t.timestamps
30
+ end
31
+
32
+ create_table :tags, force: true do |t|
33
+ t.string :name
34
+ end
35
+
36
+ create_table :sections, force: true do |t|
37
+ t.string :name
38
+ end
39
+
40
+ create_table :posts_tags, force: true do |t|
41
+ t.references :post, :tag, index: true
42
+ end
43
+
44
+ create_table :comments_tags, force: true do |t|
45
+ t.references :comment, :tag, index: true
46
+ end
47
+
48
+ create_table :currencies, id: false, force: true do |t|
49
+ t.string :code, limit: 3, null: false
50
+ t.string :name
51
+ t.timestamps
52
+ end
53
+ add_index :currencies, :code, unique: true
54
+
55
+ create_table :expense_entries, force: true do |t|
56
+ t.string :currency_code, limit: 3, null: false
57
+ t.integer :employee_id, null: false
58
+ t.decimal :cost, precision: 12, scale: 4, null: false
59
+ t.date :transaction_date
60
+ end
61
+
62
+ create_table :planets, force: true do |t|
63
+ t.string :name
64
+ t.string :description
65
+ t.integer :planet_type_id
66
+ end
67
+
68
+ create_table :planet_types, force: true do |t|
69
+ t.string :name
70
+ end
71
+
72
+ create_table :moons, force: true do |t|
73
+ t.string :name
74
+ t.string :description
75
+ t.integer :planet_id
76
+ end
77
+
78
+ create_table :preferences, force: true do |t|
79
+ t.integer :person_id
80
+ t.boolean :advanced_mode, default: false
81
+ end
82
+ end
83
+
84
+ ### MODELS
85
+ class Person < ActiveRecord::Base
86
+ has_many :posts, foreign_key: 'author_id'
87
+ has_many :comments, foreign_key: 'author_id'
88
+ has_many :expense_entries, foreign_key: 'employee_id', dependent: :restrict_with_exception
89
+
90
+ ### Validations
91
+ validates :name, presence: true
92
+ validates :date_joined, presence: true
93
+ end
94
+
95
+ class Post < ActiveRecord::Base
96
+ belongs_to :author, class_name: 'Person', foreign_key: 'author_id'
97
+ has_many :comments
98
+ has_and_belongs_to_many :tags, join_table: :posts_tags
99
+ belongs_to :section
100
+
101
+ validates :author, presence: true
102
+ end
103
+
104
+ class Comment < ActiveRecord::Base
105
+ belongs_to :author, class_name: 'Person', foreign_key: 'author_id'
106
+ belongs_to :post
107
+ has_and_belongs_to_many :tags, join_table: :comments_tags
108
+ end
109
+
110
+ class Tag < ActiveRecord::Base
111
+ has_and_belongs_to_many :posts, join_table: :posts_tags
112
+ end
113
+
114
+ class Section < ActiveRecord::Base
115
+ end
116
+
117
+ class Currency < ActiveRecord::Base
118
+ self.primary_key = :code
119
+ has_many :expense_entries, foreign_key: 'currency_code'
120
+ end
121
+
122
+ class ExpenseEntry < ActiveRecord::Base
123
+ belongs_to :employee, class_name: 'Person', foreign_key: 'employee_id'
124
+ belongs_to :currency, class_name: 'Currency', foreign_key: 'currency_code'
125
+ end
126
+
127
+ class Planet < ActiveRecord::Base
128
+ has_many :moons
129
+ has_one :planet_type
130
+ end
131
+
132
+ class PlanetType < ActiveRecord::Base
133
+ has_many :planets
134
+ end
135
+
136
+ class Moon < ActiveRecord::Base
137
+ belongs_to :planet
138
+ end
139
+
140
+ class Breed
141
+
142
+ def initialize(id = nil, name = nil)
143
+ if id.nil?
144
+ @id = $breed_data.new_id
145
+ $breed_data.add(self)
146
+ else
147
+ @id = id
148
+ end
149
+ @name = name
150
+ end
151
+
152
+ attr_accessor :id, :name
153
+
154
+ def destroy
155
+ $breed_data.remove(@id)
156
+ end
157
+
158
+ def save!
159
+ end
160
+ end
161
+
162
+ class BreedData
163
+ def initialize
164
+ @breeds = {}
165
+ end
166
+
167
+ def breeds
168
+ @breeds
169
+ end
170
+
171
+ def new_id
172
+ @breeds.keys.max + 1
173
+ end
174
+
175
+ def add(breed)
176
+ @breeds[breed.id] = breed
177
+ end
178
+
179
+ def remove(id)
180
+ @breeds.delete(id)
181
+ end
182
+
183
+ end
184
+
185
+ ### PORO Data - don't do this in a production app
186
+ $breed_data = BreedData.new
187
+ $breed_data.add(Breed.new(0, 'persian'))
188
+ $breed_data.add(Breed.new(1, 'siamese'))
189
+ $breed_data.add(Breed.new(2, 'sphinx'))
190
+ $breed_data.add(Breed.new(3, 'to_delete'))
191
+
192
+ ### CONTROLLERS
193
+ class AuthorsController < JSONAPI::ResourceController
194
+
195
+ end
196
+
197
+ class PeopleController < JSONAPI::ResourceController
198
+
199
+ end
200
+
201
+ class PostsController < JSONAPI::ResourceController
202
+ end
203
+
204
+ class TagsController < JSONAPI::ResourceController
205
+ end
206
+
207
+ class CurrenciesController < JSONAPI::ResourceController
208
+ end
209
+
210
+ class ExpenseEntriesController < JSONAPI::ResourceController
211
+ end
212
+
213
+ class BreedsController < JSONAPI::ResourceController
214
+ end
215
+
216
+ ### CONTROLLERS
217
+ module Api
218
+ module V1
219
+ class AuthorsController < JSONAPI::ResourceController
220
+ end
221
+
222
+ class PeopleController < JSONAPI::ResourceController
223
+ end
224
+
225
+ class PostsController < JSONAPI::ResourceController
226
+ end
227
+
228
+ class TagsController < JSONAPI::ResourceController
229
+ end
230
+
231
+ class CurrenciesController < JSONAPI::ResourceController
232
+ end
233
+
234
+ class ExpenseEntriesController < JSONAPI::ResourceController
235
+ end
236
+
237
+ class BreedsController < JSONAPI::ResourceController
238
+ end
239
+ end
240
+
241
+ module V2
242
+ class AuthorsController < JSONAPI::ResourceController
243
+ end
244
+
245
+ class PeopleController < JSONAPI::ResourceController
246
+ end
247
+
248
+ class PostsController < JSONAPI::ResourceController
249
+ end
250
+
251
+ class PreferencesController < JSONAPI::ResourceController
252
+ end
253
+ end
254
+
255
+ module V3
256
+ class PostsController < JSONAPI::ResourceController
257
+ end
258
+ end
259
+ end
260
+
261
+ ### RESOURCES
262
+ class PersonResource < JSONAPI::Resource
263
+ attributes :id, :name, :email, :date_joined
264
+ has_many :comments
265
+ has_many :posts
266
+
267
+ filter :name
268
+
269
+ def self.verify_custom_filter(filter, values, context = nil)
270
+ case filter
271
+ when :name
272
+ values.each do |value|
273
+ if value.length < 3
274
+ raise JSONAPI::Exceptions::InvalidFilterValue.new(filter, value)
275
+ end
276
+ end
277
+ end
278
+ return filter, values
279
+ end
280
+ end
281
+
282
+ class AuthorResource < JSONAPI::Resource
283
+ attributes :id, :name, :email
284
+ model_name 'Person'
285
+ has_many :posts
286
+
287
+ filter :name
288
+
289
+ def self.find(filters, context = nil)
290
+ resources = []
291
+
292
+ filters.each do |attr, filter|
293
+ _model_class.where("\"#{attr}\" LIKE \"%#{filter[0]}%\"").each do |object|
294
+ resources.push self.new(object)
295
+ end
296
+ end
297
+ return resources
298
+ end
299
+
300
+ def fetchable(keys, context = nil)
301
+ if (@object.id % 2) == 1
302
+ super(keys - [:email])
303
+ else
304
+ super(keys)
305
+ end
306
+ end
307
+ end
308
+
309
+ class CommentResource < JSONAPI::Resource
310
+ attributes :id, :body
311
+ has_one :post
312
+ has_one :author, class_name: 'Person'
313
+ has_many :tags
314
+ end
315
+
316
+ class TagResource < JSONAPI::Resource
317
+ attributes :id, :name
318
+
319
+ has_many :posts
320
+ end
321
+
322
+ class SectionResource < JSONAPI::Resource
323
+ attributes 'name'
324
+ end
325
+
326
+ class PostResource < JSONAPI::Resource
327
+ attribute :id
328
+ attribute :title
329
+ attribute :body
330
+ attribute :subject
331
+
332
+ has_one :author, class_name: 'Person'
333
+ has_one :section
334
+ has_many :tags, treat_as_set: true
335
+ has_many :comments, treat_as_set: false
336
+ def subject
337
+ @object.title
338
+ end
339
+
340
+ filters :title, :author, :tags, :comments
341
+ filter :id
342
+
343
+ def self.updateable(keys, context = nil)
344
+ super(keys - [:author, :subject])
345
+ end
346
+
347
+ def self.createable(keys, context = nil)
348
+ super(keys - [:subject])
349
+ end
350
+
351
+ def self.verify_custom_filter(filter, values, context = nil)
352
+ case filter
353
+ when :id
354
+ values.each do |key|
355
+ verify_key(key, context)
356
+ end
357
+ end
358
+ return filter, values
359
+ end
360
+
361
+ def self.is_num?(str)
362
+ begin
363
+ !!Integer(str)
364
+ rescue ArgumentError, TypeError
365
+ false
366
+ end
367
+ end
368
+
369
+ def self.verify_key(key, context = nil)
370
+ raise JSONAPI::Exceptions::InvalidFieldValue.new(:id, key) unless is_num?(key)
371
+ raise JSONAPI::Exceptions::RecordNotFound.new(key) unless find_by_key(key)
372
+ return key
373
+ end
374
+ end
375
+
376
+ class CurrencyResource < JSONAPI::Resource
377
+ key :code
378
+ attributes :code, :name
379
+
380
+ routing_options :param => :code
381
+
382
+ has_many :expense_entries
383
+ end
384
+
385
+ class ExpenseEntryResource < JSONAPI::Resource
386
+ attributes :id, :cost, :transaction_date
387
+
388
+ has_one :currency, class_name: 'Currency', key: 'currency_code'
389
+ has_one :employee
390
+ end
391
+
392
+ class BreedResource < JSONAPI::Resource
393
+ attributes :id, :name
394
+
395
+ def self.find(attrs, context = nil)
396
+ breeds = []
397
+ $breed_data.breeds.values.each do |breed|
398
+ breeds.push(BreedResource.new(breed))
399
+ end
400
+ breeds
401
+ end
402
+
403
+ def self.find_by_key(id, context = nil)
404
+ BreedResource.new($breed_data.breeds[id.to_i])
405
+ end
406
+ end
407
+
408
+ class PlanetResource < JSONAPI::Resource
409
+ attribute :id
410
+ attribute :name
411
+ attribute :description
412
+
413
+ has_many :moons
414
+ has_one :planet_type
415
+ end
416
+
417
+ class PlanetTypeResource < JSONAPI::Resource
418
+ attribute :name
419
+ has_many :planets
420
+ end
421
+
422
+ class MoonResource < JSONAPI::Resource
423
+ attribute :id
424
+ attribute :name
425
+ attribute :description
426
+
427
+ has_one :planet
428
+ end
429
+
430
+ class PreferencesResource < JSONAPI::Resource
431
+ attribute :id
432
+ attribute :advanced_mode
433
+
434
+ has_one :author, class_name: 'Person'
435
+ has_many :friends, class_name: 'Person'
436
+ end
437
+
438
+ ### DATA
439
+ javascript = Section.create(name: 'javascript')
440
+ ruby = Section.create(name: 'ruby')
441
+
442
+ a = Person.create(name: 'Joe Author',
443
+ email: 'joe@xyz.fake',
444
+ date_joined: DateTime.parse('2013-08-07 20:25:00 UTC +00:00'))
445
+
446
+ b = Person.create(name: 'Fred Reader',
447
+ email: 'fred@xyz.fake',
448
+ date_joined: DateTime.parse('2013-10-31 20:25:00 UTC +00:00'))
449
+
450
+ c = Person.create(name: 'Lazy Author',
451
+ email: 'lazy@xyz.fake',
452
+ date_joined: DateTime.parse('2013-10-31 21:25:00 UTC +00:00'))
453
+
454
+ d = Person.create(name: 'Tag Crazy Author',
455
+ email: 'taggy@xyz.fake',
456
+ date_joined: DateTime.parse('2013-11-30 4:20:00 UTC +00:00'))
457
+
458
+ short_tag = Tag.create(name: 'short')
459
+ whiny_tag = Tag.create(name: 'whiny')
460
+ grumpy_tag = Tag.create(name: 'grumpy')
461
+ happy_tag = Tag.create(name: 'happy')
462
+ jr_tag = Tag.create(name: 'JR')
463
+
464
+ silly_tag = Tag.create(name: 'silly')
465
+ sleepy_tag = Tag.create(name: 'sleepy')
466
+ goofy_tag = Tag.create(name: 'goofy')
467
+ wacky_tag = Tag.create(name: 'wacky')
468
+
469
+ # id:1
470
+ Post.create(title: 'New post',
471
+ body: 'A body!!!',
472
+ author_id: a.id).tap do |post|
473
+
474
+ post.tags.concat short_tag, whiny_tag, grumpy_tag
475
+
476
+ post.comments.create(body: 'what a dumb post', author_id: a.id, post_id: post.id).tap do |comment|
477
+ comment.tags.concat whiny_tag, short_tag
478
+ end
479
+
480
+ post.comments.create(body: 'i liked it', author_id: b.id, post_id: post.id).tap do |comment|
481
+ comment.tags.concat happy_tag, short_tag
482
+ end
483
+ end
484
+
485
+ # id:2
486
+ Post.create(title: 'JR Solves your serialization woes!',
487
+ body: 'Use JR',
488
+ author_id: a.id,
489
+ section: Section.create(name: 'ruby')).tap do |post|
490
+
491
+ post.tags.concat jr_tag
492
+
493
+ post.comments.create(body: 'Thanks man. Great post. But what is JR?', author_id: b.id, post_id: post.id).tap do |comment|
494
+ comment.tags.concat jr_tag
495
+ end
496
+ end
497
+
498
+ # id:3
499
+ Post.create(title: 'Update This Later',
500
+ body: 'AAAA',
501
+ author_id: c.id)
502
+
503
+ # id:4
504
+ Post.create(title: 'Delete This Later - Single',
505
+ body: 'AAAA',
506
+ author_id: c.id)
507
+
508
+ # id:5
509
+ Post.create(title: 'Delete This Later - Multiple1',
510
+ body: 'AAAA',
511
+ author_id: c.id)
512
+
513
+ # id:6
514
+ Post.create(title: 'Delete This Later - Multiple2',
515
+ body: 'AAAA',
516
+ author_id: c.id)
517
+
518
+ # id:7
519
+ Post.create(title: 'Delete This Later - Single2',
520
+ body: 'AAAA',
521
+ author_id: c.id)
522
+
523
+ # id:8
524
+ Post.create(title: 'Delete This Later - Multiple2-1',
525
+ body: 'AAAA',
526
+ author_id: c.id)
527
+
528
+ # id:9
529
+ Post.create(title: 'Delete This Later - Multiple2-2',
530
+ body: 'AAAA',
531
+ author_id: c.id)
532
+
533
+ # id:9
534
+ Post.create(title: 'Update This Later - Multiple',
535
+ body: 'AAAA',
536
+ author_id: c.id)
537
+
538
+ # id:10
539
+ Post.create(title: 'JR How To',
540
+ body: 'Use JR to write API apps',
541
+ author_id: a.id).tap do |post|
542
+ post.tags.concat jr_tag
543
+ end
544
+
545
+ Currency.create(code: 'USD', name: 'United States Dollar')
546
+ Currency.create(code: 'EUR', name: 'Euro Member Countries')
547
+
548
+ ExpenseEntry.create(currency_code: 'USD',
549
+ employee_id: c.id,
550
+ cost: '12.05',
551
+ transaction_date: DateTime.parse('2014-04-15 12:13:14 UTC +00:00'))
552
+
553
+ ExpenseEntry.create(currency_code: 'USD',
554
+ employee_id: c.id,
555
+ cost: '12.06',
556
+ transaction_date: DateTime.parse('2014-04-15 12:13:15 UTC +00:00'))
557
+
558
+ Post.create(title: 'Tagged up post 1',
559
+ body: 'AAAA',
560
+ author_id: d.id,
561
+ tag_ids: [6,7,8,9]
562
+ )
563
+
564
+ Post.create(title: 'Tagged up post 2',
565
+ body: 'BBBB',
566
+ author_id: d.id,
567
+ tag_ids: [6,7,8,9]
568
+ )
569
+
570
+ gas_giant = PlanetType.create(name: 'Gas Giant')
571
+ planetoid = PlanetType.create(name: 'Planetoid')
572
+ terrestrial = PlanetType.create(name: 'Terrestrial')
573
+ sulfuric = PlanetType.create(name: 'Sulfuric')
574
+ unknown = PlanetType.create(name: 'unknown')
575
+
576
+ saturn = Planet.create(name: 'Satern',
577
+ description: 'Saturn is the sixth planet from the Sun and the second largest planet in the Solar System, after Jupiter.',
578
+ planet_type_id: planetoid.id)
579
+ titan = Moon.create(name:'Titan', description: 'Best known of the Saturn moons.', planet_id: saturn.id)
580
+ pluto = Planet.create(name: 'Pluto', description: 'Pluto is the smallest planet.', planet_type_id: planetoid.id)
581
+ uranus = Planet.create(name: 'Uranus', description: 'Insert adolescent jokes here.', planet_type_id: gas_giant.id)
582
+ jupiter = Planet.create(name: 'Jupiter', description: 'A gas giant.', planet_type_id: gas_giant.id)
583
+ betax = Planet.create(name: 'Beta X', description: 'Newly discovered Planet X', planet_type_id: unknown.id)
584
+ betay = Planet.create(name: 'Beta X', description: 'Newly discovered Planet Y', planet_type_id: unknown.id)
585
+ betaz = Planet.create(name: 'Beta X', description: 'Newly discovered Planet Z', planet_type_id: unknown.id)