fun_with_json_api 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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +28 -0
  4. data/config/locales/fun_with_json_api.en.yml +13 -0
  5. data/lib/fun_with_json_api/attribute.rb +38 -0
  6. data/lib/fun_with_json_api/attributes/boolean_attribute.rb +24 -0
  7. data/lib/fun_with_json_api/attributes/date_attribute.rb +22 -0
  8. data/lib/fun_with_json_api/attributes/datetime_attribute.rb +20 -0
  9. data/lib/fun_with_json_api/attributes/decimal_attribute.rb +23 -0
  10. data/lib/fun_with_json_api/attributes/float_attribute.rb +20 -0
  11. data/lib/fun_with_json_api/attributes/integer_attribute.rb +20 -0
  12. data/lib/fun_with_json_api/attributes/relationship.rb +73 -0
  13. data/lib/fun_with_json_api/attributes/relationship_collection.rb +99 -0
  14. data/lib/fun_with_json_api/attributes/string_attribute.rb +9 -0
  15. data/lib/fun_with_json_api/controller_methods.rb +12 -0
  16. data/lib/fun_with_json_api/deserializer.rb +70 -0
  17. data/lib/fun_with_json_api/deserializer_class_methods.rb +83 -0
  18. data/lib/fun_with_json_api/deserializer_config_builder.rb +48 -0
  19. data/lib/fun_with_json_api/exception.rb +27 -0
  20. data/lib/fun_with_json_api/exception_payload.rb +29 -0
  21. data/lib/fun_with_json_api/exception_payload_serializer.rb +17 -0
  22. data/lib/fun_with_json_api/exception_serializer.rb +15 -0
  23. data/lib/fun_with_json_api/exceptions/invalid_attribute.rb +13 -0
  24. data/lib/fun_with_json_api/exceptions/invalid_document.rb +12 -0
  25. data/lib/fun_with_json_api/exceptions/invalid_relationship.rb +13 -0
  26. data/lib/fun_with_json_api/exceptions/missing_relationship.rb +15 -0
  27. data/lib/fun_with_json_api/pre_deserializer.rb +61 -0
  28. data/lib/fun_with_json_api/railtie.rb +11 -0
  29. data/lib/fun_with_json_api/version.rb +3 -0
  30. data/lib/fun_with_json_api.rb +24 -0
  31. data/lib/tasks/fun_with_json_api_tasks.rake +4 -0
  32. data/spec/dummy/README.rdoc +28 -0
  33. data/spec/dummy/Rakefile +6 -0
  34. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  35. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  36. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  37. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  38. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  39. data/spec/dummy/bin/bundle +3 -0
  40. data/spec/dummy/bin/rails +4 -0
  41. data/spec/dummy/bin/rake +4 -0
  42. data/spec/dummy/bin/setup +29 -0
  43. data/spec/dummy/config/application.rb +25 -0
  44. data/spec/dummy/config/boot.rb +5 -0
  45. data/spec/dummy/config/database.yml +25 -0
  46. data/spec/dummy/config/environment.rb +5 -0
  47. data/spec/dummy/config/environments/development.rb +41 -0
  48. data/spec/dummy/config/environments/production.rb +80 -0
  49. data/spec/dummy/config/environments/test.rb +42 -0
  50. data/spec/dummy/config/initializers/assets.rb +11 -0
  51. data/spec/dummy/config/initializers/backtrace_silencers.rb +9 -0
  52. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  53. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  54. data/spec/dummy/config/initializers/inflections.rb +16 -0
  55. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  56. data/spec/dummy/config/initializers/session_store.rb +3 -0
  57. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/dummy/config/locales/en.yml +23 -0
  59. data/spec/dummy/config/routes.rb +56 -0
  60. data/spec/dummy/config/secrets.yml +22 -0
  61. data/spec/dummy/config.ru +4 -0
  62. data/spec/dummy/log/test.log +37839 -0
  63. data/spec/dummy/public/404.html +67 -0
  64. data/spec/dummy/public/422.html +67 -0
  65. data/spec/dummy/public/500.html +66 -0
  66. data/spec/dummy/public/favicon.ico +0 -0
  67. data/spec/example_spec.rb +64 -0
  68. data/spec/fixtures/active_record.rb +65 -0
  69. data/spec/fun_with_json_api/controller_methods_spec.rb +123 -0
  70. data/spec/fun_with_json_api/deserializer_class_methods_spec.rb +52 -0
  71. data/spec/fun_with_json_api/deserializer_spec.rb +450 -0
  72. data/spec/fun_with_json_api/exception_spec.rb +77 -0
  73. data/spec/fun_with_json_api/pre_deserializer_spec.rb +287 -0
  74. data/spec/fun_with_json_api_spec.rb +55 -0
  75. data/spec/spec_helper.rb +33 -0
  76. metadata +275 -0
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/404.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The page you were looking for doesn't exist.</h1>
62
+ <p>You may have mistyped the address or the page may have moved.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/422.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The change you wanted was rejected.</h1>
62
+ <p>Maybe you tried to change something you didn't have access to.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Example' do
4
+ xit 'prints out the thing' do
5
+ params = {
6
+ id: 'zorglub',
7
+ title: 'Ember Hamster',
8
+ published_at: '2016-03-12',
9
+ enabled_at: '2016-03-12T17:19:28+11:00',
10
+ promoted: false,
11
+ times_read: '414',
12
+ author_id: nil,
13
+ editor_id: '9',
14
+ comment_ids: %w(1 2)
15
+ }
16
+
17
+ puts "attributes: #{ArticleDeserializer.attributes.map(&:name).inspect}"
18
+
19
+ deserializer = ArticleDeserializer.create
20
+ deserialized_params = deserializer.sanitize_params(params)
21
+ puts "title: #{deserialized_params[:title].inspect}"
22
+ puts "published_at: #{deserialized_params[:published_at].inspect}"
23
+ puts "enabled_at: #{deserialized_params[:enabled_at].inspect}"
24
+ puts "times_read: #{deserialized_params[:times_read].inspect}"
25
+ puts deserialized_params.inspect
26
+
27
+ expect(deserializer.type).to eq 'articles'
28
+ expect(deserializer.resource_class).to eq Article
29
+ end
30
+
31
+ xit 'deserializes params' do
32
+ document_data = {
33
+ # id: 'zorglub',
34
+ type: 'articles',
35
+ attributes: {
36
+ title: 'Ember Hamster',
37
+ date: '2016-03-12',
38
+ enabled_at: '2016-03-12T13:45:40+09:30',
39
+ promoted: false,
40
+ times_read: '414'
41
+ },
42
+ relationships: {
43
+ author: { data: nil },
44
+ editor: { data: { id: 'demouser', type: 'persons' } },
45
+ comments: { data: [{ id: '1', type: 'comments' }, { id: '2', type: 'comments' }] }
46
+ }
47
+ }
48
+ document = { data: document_data }.as_json
49
+
50
+ document_params = FunWithJsonApi.deserialize(document, ArticleDeserializer)
51
+ expect(document_params).to eq(
52
+ # id: Article.find(code: 'zorglub').id,
53
+ title: 'Ember Hamster',
54
+ published_at: Date.new(2016, 03, 12),
55
+ enabled_at: DateTime.new(2016, 03, 12, 4, 15, 40, 0),
56
+ promoted: false,
57
+ times_read: 414,
58
+ author_id: nil,
59
+ editor_id: Person.find_by!(username: 'demouser').id,
60
+ comment_ids: [1, 2]
61
+ )
62
+ puts "deserialized: #{document_params.inspect}"
63
+ end
64
+ end
@@ -0,0 +1,65 @@
1
+ require 'active_record'
2
+
3
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
4
+ ActiveRecord::Schema.define do
5
+ create_table :posts, force: true do |t|
6
+ t.string :title
7
+ t.text :body
8
+ t.references :author
9
+ t.timestamps null: false
10
+ end
11
+ create_table :authors, force: true do |t|
12
+ t.string :name
13
+ t.string :code
14
+ t.timestamps null: false
15
+ end
16
+ create_table :comments, force: true do |t|
17
+ t.text :contents
18
+ t.references :author
19
+ t.references :post
20
+ t.timestamp null: false
21
+ end
22
+ end
23
+
24
+ module ARModels
25
+ class Post < ActiveRecord::Base
26
+ has_many :comments
27
+ belongs_to :author
28
+ end
29
+
30
+ class Comment < ActiveRecord::Base
31
+ belongs_to :post
32
+ belongs_to :author
33
+ end
34
+
35
+ class Author < ActiveRecord::Base
36
+ has_many :posts
37
+ end
38
+
39
+ class PostDeserializer < FunWithJsonApi::Deserializer
40
+ resource_class Post
41
+
42
+ attribute :title
43
+ attribute :body
44
+
45
+ has_many :comments, -> { CommentDeserializer }
46
+ belongs_to :author, -> { AuthorDeserializer }
47
+ end
48
+
49
+ class CommentDeserializer < FunWithJsonApi::Deserializer
50
+ resource_class Comment
51
+
52
+ attribute :contents
53
+
54
+ belongs_to :author, -> { AuthorDeserializer }
55
+ end
56
+
57
+ class AuthorDeserializer < FunWithJsonApi::Deserializer
58
+ type 'person'
59
+ resource_class Author
60
+
61
+ attribute :name
62
+
63
+ has_many :posts, -> { PostDeserializer }
64
+ end
65
+ end
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+
3
+ describe FunWithJsonApi::ControllerMethods, type: :controller do
4
+ describe '#render_fun_with_json_api_exception' do
5
+ context 'with a controller that raises an exception' do
6
+ controller do
7
+ include FunWithJsonApi::ControllerMethods
8
+
9
+ rescue_from FunWithJsonApi::Exception, with: :render_fun_with_json_api_exception
10
+
11
+ attr_accessor :fun_with_json_api_exception
12
+
13
+ def index
14
+ raise fun_with_json_api_exception
15
+ end
16
+ end
17
+
18
+ let(:payload) { [] }
19
+ let(:exception) { FunWithJsonApi::Exception.new('document is invalid!', payload) }
20
+ before { controller.fun_with_json_api_exception = exception }
21
+
22
+ context 'with a full exception payload' do
23
+ let(:payload) do
24
+ FunWithJsonApi::ExceptionPayload.new(
25
+ id: '123',
26
+ status: '403',
27
+ code: 'text_exception',
28
+ title: 'Test Example Title',
29
+ detail: 'Test Example Details',
30
+ pointer: '/data/attributes/example',
31
+ parameter: 'example'
32
+ )
33
+ end
34
+
35
+ it 'returns a json api error status' do
36
+ get :index
37
+ expect(response.status).to eq 403
38
+ end
39
+
40
+ it 'renders the exception payload as a json api errors response ' do
41
+ get :index
42
+ expect(JSON.parse(response.body)).to eq(
43
+ 'errors' => [
44
+ {
45
+ 'id' => '123',
46
+ 'status' => '403',
47
+ 'code' => 'text_exception',
48
+ 'title' => 'Test Example Title',
49
+ 'detail' => 'Test Example Details',
50
+ 'source' => {
51
+ 'pointer' => '/data/attributes/example',
52
+ 'parameter' => 'example'
53
+ }
54
+ }
55
+ ]
56
+ )
57
+ end
58
+ end
59
+
60
+ context 'when the exception has multiple payloads' do
61
+ let(:payload) do
62
+ [
63
+ FunWithJsonApi::ExceptionPayload.new(
64
+ status: '403',
65
+ code: 'text_exception'
66
+ ),
67
+ FunWithJsonApi::ExceptionPayload.new(
68
+ status: '403',
69
+ detail: 'This is an example'
70
+ )
71
+ ]
72
+ end
73
+
74
+ it 'returns a json api error status' do
75
+ get :index
76
+ expect(response.status).to eq 403
77
+ end
78
+
79
+ it 'renders all exception payload items ' do
80
+ get :index
81
+ expect(JSON.parse(response.body)).to eq(
82
+ 'errors' => [
83
+ {
84
+ 'status' => '403',
85
+ 'code' => 'text_exception'
86
+ },
87
+ {
88
+ 'status' => '403',
89
+ 'detail' => 'This is an example'
90
+ }
91
+ ]
92
+ )
93
+ end
94
+ end
95
+
96
+ context 'with a partial ExceptionPayload' do
97
+ let(:payload) do
98
+ FunWithJsonApi::ExceptionPayload.new(
99
+ status: '422',
100
+ code: 'text_exception'
101
+ )
102
+ end
103
+
104
+ it 'returns a json api error status' do
105
+ get :index
106
+ expect(response.status).to eq 422
107
+ end
108
+
109
+ it 'only renders the non-nil params ' do
110
+ get :index
111
+ expect(JSON.parse(response.body)).to eq(
112
+ 'errors' => [
113
+ {
114
+ 'status' => '422',
115
+ 'code' => 'text_exception'
116
+ }
117
+ ]
118
+ )
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end