grape_fast_jsonapi 0.1.0 → 0.2.0
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.
- checksums.yaml +5 -5
- data/.circleci/config.yml +57 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +173 -0
- data/README.md +28 -4
- data/grape_fast_jsonapi.gemspec +3 -5
- data/lib/grape_fast_jsonapi/formatter.rb +14 -1
- data/lib/grape_fast_jsonapi/parser.rb +178 -0
- data/lib/grape_fast_jsonapi/version.rb +1 -1
- data/lib/grape_fast_jsonapi.rb +4 -0
- data/spec/lib/grape_fast_jsonapi/formatter_spec.rb +88 -0
- data/spec/lib/grape_fast_jsonapi/parser_spec.rb +131 -0
- data/spec/lib/grape_fast_jsonapi/version_spec.rb +5 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/support/models/blog_post.rb +18 -0
- data/spec/support/models/user.rb +28 -0
- data/spec/support/serializers/blog_post_serializer.rb +9 -0
- data/spec/support/serializers/user_serializer.rb +9 -0
- metadata +33 -41
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 38ad3fb865f43aa0c089abf0136617191d3dbf8fa10d3dff3798537312026851
|
|
4
|
+
data.tar.gz: 44f09504170ee1b3ec39be3d11784adaf5255b867693c77850d5089b2b2cecf5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c6d628fe38e31362949ee2c9e7fbf9d866fe5e99ee910aa16f8dc533da1ee54ec5e8af949d688b194fbe783d258fe5ac09e20ee5a23d60ba8b1aa61335f4d1f7
|
|
7
|
+
data.tar.gz: c6f7ec14e0e3d67e9dd707dd99b563408d62526f4188c114198c53a8b053a0075c2c7f98bea028cf1baa797a613465ba7b0b8be16e431624bc961ae6dd00804c
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Ruby CircleCI 2.0 configuration file
|
|
2
|
+
#
|
|
3
|
+
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
|
|
4
|
+
#
|
|
5
|
+
version: 2
|
|
6
|
+
jobs:
|
|
7
|
+
build:
|
|
8
|
+
docker:
|
|
9
|
+
# specify the version you desire here
|
|
10
|
+
- image: circleci/ruby:2.5.3-node-browsers
|
|
11
|
+
environment:
|
|
12
|
+
RAILS_ENV: test
|
|
13
|
+
|
|
14
|
+
# Specify service dependencies here if necessary
|
|
15
|
+
# CircleCI maintains a library of pre-built images
|
|
16
|
+
# documented at https://circleci.com/docs/2.0/circleci-images/
|
|
17
|
+
|
|
18
|
+
working_directory: ~/repo
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- checkout
|
|
22
|
+
|
|
23
|
+
# Download and cache dependencies
|
|
24
|
+
- restore_cache:
|
|
25
|
+
keys:
|
|
26
|
+
- v1-dependencies-{{ checksum "Gemfile.lock" }}
|
|
27
|
+
# fallback to using the latest cache if no exact match is found
|
|
28
|
+
- v1-dependencies-
|
|
29
|
+
|
|
30
|
+
- run:
|
|
31
|
+
name: install gems
|
|
32
|
+
command: |
|
|
33
|
+
bundle install --jobs=4 --retry=3 --path vendor/bundle
|
|
34
|
+
|
|
35
|
+
- save_cache:
|
|
36
|
+
paths:
|
|
37
|
+
- ./vendor/bundle
|
|
38
|
+
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
|
|
39
|
+
|
|
40
|
+
# run tests!
|
|
41
|
+
- run:
|
|
42
|
+
name: run tests
|
|
43
|
+
command: |
|
|
44
|
+
mkdir /tmp/test-results
|
|
45
|
+
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)"
|
|
46
|
+
|
|
47
|
+
bundle exec rspec --format progress \
|
|
48
|
+
--out /tmp/test-results/rspec.xml \
|
|
49
|
+
--format progress \
|
|
50
|
+
$TEST_FILES
|
|
51
|
+
|
|
52
|
+
# collect reports
|
|
53
|
+
- store_test_results:
|
|
54
|
+
path: /tmp/test-results
|
|
55
|
+
- store_artifacts:
|
|
56
|
+
path: /tmp/test-results
|
|
57
|
+
destination: test-results
|
data/.rspec
ADDED
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
## Changelog
|
|
2
|
+
|
|
3
|
+
### 0.2.1 (Next)
|
|
4
|
+
|
|
5
|
+
* Your contribution here.
|
|
6
|
+
|
|
7
|
+
### v0.2.0 (February 8, 2019)
|
|
8
|
+
|
|
9
|
+
* [#5](https://github.com/EmCousin/grape_fast_jsonapi/pull/5): Provide custom Grape Swagger parser for fast_jsonapi object serializers, as well as unit test coverage - [@EmCousin](https://github.com/EmCousin)
|
|
10
|
+
* [#6](https://github.com/EmCousin/grape_fast_jsonapi/pull/6) - Fix to make the parser compatible with latest version of fast_jsonapi (1.5 at date) - @rromanchuk](https://github.com/rromanchuk).
|
|
11
|
+
|
|
12
|
+
### v0.1.0
|
|
13
|
+
|
|
14
|
+
* Initial public release - [@EmCousin](https://github.com/EmCousin).
|
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
grape_fast_jsonapi (0.2.0)
|
|
5
|
+
fast_jsonapi (>= 1.5)
|
|
6
|
+
grape
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: https://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
actioncable (5.2.2)
|
|
12
|
+
actionpack (= 5.2.2)
|
|
13
|
+
nio4r (~> 2.0)
|
|
14
|
+
websocket-driver (>= 0.6.1)
|
|
15
|
+
actionmailer (5.2.2)
|
|
16
|
+
actionpack (= 5.2.2)
|
|
17
|
+
actionview (= 5.2.2)
|
|
18
|
+
activejob (= 5.2.2)
|
|
19
|
+
mail (~> 2.5, >= 2.5.4)
|
|
20
|
+
rails-dom-testing (~> 2.0)
|
|
21
|
+
actionpack (5.2.2)
|
|
22
|
+
actionview (= 5.2.2)
|
|
23
|
+
activesupport (= 5.2.2)
|
|
24
|
+
rack (~> 2.0)
|
|
25
|
+
rack-test (>= 0.6.3)
|
|
26
|
+
rails-dom-testing (~> 2.0)
|
|
27
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
28
|
+
actionview (5.2.2)
|
|
29
|
+
activesupport (= 5.2.2)
|
|
30
|
+
builder (~> 3.1)
|
|
31
|
+
erubi (~> 1.4)
|
|
32
|
+
rails-dom-testing (~> 2.0)
|
|
33
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
34
|
+
activejob (5.2.2)
|
|
35
|
+
activesupport (= 5.2.2)
|
|
36
|
+
globalid (>= 0.3.6)
|
|
37
|
+
activemodel (5.2.2)
|
|
38
|
+
activesupport (= 5.2.2)
|
|
39
|
+
activerecord (5.2.2)
|
|
40
|
+
activemodel (= 5.2.2)
|
|
41
|
+
activesupport (= 5.2.2)
|
|
42
|
+
arel (>= 9.0)
|
|
43
|
+
activestorage (5.2.2)
|
|
44
|
+
actionpack (= 5.2.2)
|
|
45
|
+
activerecord (= 5.2.2)
|
|
46
|
+
marcel (~> 0.3.1)
|
|
47
|
+
activesupport (5.2.2)
|
|
48
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
49
|
+
i18n (>= 0.7, < 2)
|
|
50
|
+
minitest (~> 5.1)
|
|
51
|
+
tzinfo (~> 1.1)
|
|
52
|
+
arel (9.0.0)
|
|
53
|
+
axiom-types (0.1.1)
|
|
54
|
+
descendants_tracker (~> 0.0.4)
|
|
55
|
+
ice_nine (~> 0.11.0)
|
|
56
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
|
57
|
+
builder (3.2.3)
|
|
58
|
+
coercible (1.0.0)
|
|
59
|
+
descendants_tracker (~> 0.0.1)
|
|
60
|
+
concurrent-ruby (1.1.4)
|
|
61
|
+
crass (1.0.4)
|
|
62
|
+
descendants_tracker (0.0.4)
|
|
63
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
|
64
|
+
diff-lcs (1.3)
|
|
65
|
+
equalizer (0.0.11)
|
|
66
|
+
erubi (1.8.0)
|
|
67
|
+
fast_jsonapi (1.5)
|
|
68
|
+
activesupport (>= 4.2)
|
|
69
|
+
globalid (0.4.2)
|
|
70
|
+
activesupport (>= 4.2.0)
|
|
71
|
+
grape (1.2.3)
|
|
72
|
+
activesupport
|
|
73
|
+
builder
|
|
74
|
+
mustermann-grape (~> 1.0.0)
|
|
75
|
+
rack (>= 1.3.0)
|
|
76
|
+
rack-accept
|
|
77
|
+
virtus (>= 1.0.0)
|
|
78
|
+
i18n (1.5.3)
|
|
79
|
+
concurrent-ruby (~> 1.0)
|
|
80
|
+
ice_nine (0.11.2)
|
|
81
|
+
loofah (2.2.3)
|
|
82
|
+
crass (~> 1.0.2)
|
|
83
|
+
nokogiri (>= 1.5.9)
|
|
84
|
+
mail (2.7.1)
|
|
85
|
+
mini_mime (>= 0.1.1)
|
|
86
|
+
marcel (0.3.3)
|
|
87
|
+
mimemagic (~> 0.3.2)
|
|
88
|
+
method_source (0.9.2)
|
|
89
|
+
mimemagic (0.3.3)
|
|
90
|
+
mini_mime (1.0.1)
|
|
91
|
+
mini_portile2 (2.4.0)
|
|
92
|
+
minitest (5.11.3)
|
|
93
|
+
mustermann (1.0.3)
|
|
94
|
+
mustermann-grape (1.0.0)
|
|
95
|
+
mustermann (~> 1.0.0)
|
|
96
|
+
nio4r (2.3.1)
|
|
97
|
+
nokogiri (1.10.1)
|
|
98
|
+
mini_portile2 (~> 2.4.0)
|
|
99
|
+
rack (2.0.6)
|
|
100
|
+
rack-accept (0.4.5)
|
|
101
|
+
rack (>= 0.4)
|
|
102
|
+
rack-test (1.1.0)
|
|
103
|
+
rack (>= 1.0, < 3)
|
|
104
|
+
rails (5.2.2)
|
|
105
|
+
actioncable (= 5.2.2)
|
|
106
|
+
actionmailer (= 5.2.2)
|
|
107
|
+
actionpack (= 5.2.2)
|
|
108
|
+
actionview (= 5.2.2)
|
|
109
|
+
activejob (= 5.2.2)
|
|
110
|
+
activemodel (= 5.2.2)
|
|
111
|
+
activerecord (= 5.2.2)
|
|
112
|
+
activestorage (= 5.2.2)
|
|
113
|
+
activesupport (= 5.2.2)
|
|
114
|
+
bundler (>= 1.3.0)
|
|
115
|
+
railties (= 5.2.2)
|
|
116
|
+
sprockets-rails (>= 2.0.0)
|
|
117
|
+
rails-dom-testing (2.0.3)
|
|
118
|
+
activesupport (>= 4.2.0)
|
|
119
|
+
nokogiri (>= 1.6)
|
|
120
|
+
rails-html-sanitizer (1.0.4)
|
|
121
|
+
loofah (~> 2.2, >= 2.2.2)
|
|
122
|
+
railties (5.2.2)
|
|
123
|
+
actionpack (= 5.2.2)
|
|
124
|
+
activesupport (= 5.2.2)
|
|
125
|
+
method_source
|
|
126
|
+
rake (>= 0.8.7)
|
|
127
|
+
thor (>= 0.19.0, < 2.0)
|
|
128
|
+
rake (12.3.2)
|
|
129
|
+
rspec (3.8.0)
|
|
130
|
+
rspec-core (~> 3.8.0)
|
|
131
|
+
rspec-expectations (~> 3.8.0)
|
|
132
|
+
rspec-mocks (~> 3.8.0)
|
|
133
|
+
rspec-core (3.8.0)
|
|
134
|
+
rspec-support (~> 3.8.0)
|
|
135
|
+
rspec-expectations (3.8.2)
|
|
136
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
137
|
+
rspec-support (~> 3.8.0)
|
|
138
|
+
rspec-mocks (3.8.0)
|
|
139
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
140
|
+
rspec-support (~> 3.8.0)
|
|
141
|
+
rspec-support (3.8.0)
|
|
142
|
+
sprockets (3.7.2)
|
|
143
|
+
concurrent-ruby (~> 1.0)
|
|
144
|
+
rack (> 1, < 3)
|
|
145
|
+
sprockets-rails (3.2.1)
|
|
146
|
+
actionpack (>= 4.0)
|
|
147
|
+
activesupport (>= 4.0)
|
|
148
|
+
sprockets (>= 3.0.0)
|
|
149
|
+
thor (0.20.3)
|
|
150
|
+
thread_safe (0.3.6)
|
|
151
|
+
tzinfo (1.2.5)
|
|
152
|
+
thread_safe (~> 0.1)
|
|
153
|
+
virtus (1.0.5)
|
|
154
|
+
axiom-types (~> 0.1)
|
|
155
|
+
coercible (~> 1.0)
|
|
156
|
+
descendants_tracker (~> 0.0, >= 0.0.3)
|
|
157
|
+
equalizer (~> 0.0, >= 0.0.9)
|
|
158
|
+
websocket-driver (0.7.0)
|
|
159
|
+
websocket-extensions (>= 0.1.0)
|
|
160
|
+
websocket-extensions (0.1.3)
|
|
161
|
+
|
|
162
|
+
PLATFORMS
|
|
163
|
+
ruby
|
|
164
|
+
|
|
165
|
+
DEPENDENCIES
|
|
166
|
+
fast_jsonapi
|
|
167
|
+
grape
|
|
168
|
+
grape_fast_jsonapi!
|
|
169
|
+
rails (>= 4.2.0)
|
|
170
|
+
rspec (~> 3.7)
|
|
171
|
+
|
|
172
|
+
BUNDLED WITH
|
|
173
|
+
1.16.6
|
data/README.md
CHANGED
|
@@ -8,14 +8,11 @@ Add the `grape` and `grape_fast_jsonapi` gems to Gemfile.
|
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
10
|
gem 'grape'
|
|
11
|
-
|
|
12
|
-
gem 'grape_fast_jsonapi', git: 'git@github.com:EmCousin/grape_fast_jsonapi.git'
|
|
11
|
+
gem 'grape_fast_jsonapi'
|
|
13
12
|
```
|
|
14
13
|
|
|
15
14
|
## Usage
|
|
16
15
|
|
|
17
|
-
### Require grape_fast_jsonapi
|
|
18
|
-
|
|
19
16
|
### Tell your API to use Grape::Formatter::FastJsonapi
|
|
20
17
|
|
|
21
18
|
```ruby
|
|
@@ -34,6 +31,33 @@ get "/" do
|
|
|
34
31
|
end
|
|
35
32
|
```
|
|
36
33
|
|
|
34
|
+
### Model parser for response documentation
|
|
35
|
+
|
|
36
|
+
When using Grape with Swagger via [grape-swagger](https://github.com/ruby-grape/grape-swagger), you can generate response documentation automatically via the provided following model parser:
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
# FastJsonapi serializer example
|
|
40
|
+
# app/serializers/user_serializer.rb
|
|
41
|
+
class UserSerializer
|
|
42
|
+
include FastJsonapi::ObjectSerializer
|
|
43
|
+
|
|
44
|
+
set_type :user
|
|
45
|
+
has_many :orders
|
|
46
|
+
|
|
47
|
+
attributes :name, :email
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# config/initializers/grape_swagger.rb
|
|
51
|
+
GrapeSwagger.model_parsers.register(GrapeSwagger::FastJsonapi::Parser, UserSerializer)
|
|
52
|
+
|
|
53
|
+
# Your grape API endpoint
|
|
54
|
+
desc 'Get current user',
|
|
55
|
+
success: { code: 200, model: UserSerializer, message: 'The current user' }
|
|
56
|
+
# [...]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Note that you **need** the `grape-swagger` gem for this to work, otherwise it will throw an error.
|
|
60
|
+
|
|
37
61
|
## Credit
|
|
38
62
|
|
|
39
63
|
Code adapted from [grape-jsonapi-resources](https://github.com/cdunn/grape-jsonapi-resources)
|
data/grape_fast_jsonapi.gemspec
CHANGED
|
@@ -19,10 +19,8 @@ Gem::Specification.new do |gem|
|
|
|
19
19
|
gem.licenses = ['MIT']
|
|
20
20
|
|
|
21
21
|
gem.add_dependency 'grape'
|
|
22
|
-
gem.add_dependency 'fast_jsonapi'
|
|
22
|
+
gem.add_dependency 'fast_jsonapi', '>= 1.5'
|
|
23
23
|
|
|
24
|
-
gem.add_development_dependency 'rails', '>=
|
|
25
|
-
gem.add_development_dependency 'rspec'
|
|
26
|
-
gem.add_development_dependency 'rack-test'
|
|
27
|
-
gem.add_development_dependency 'rake'
|
|
24
|
+
gem.add_development_dependency 'rails', '>= 4.2.0'
|
|
25
|
+
gem.add_development_dependency 'rspec', '~> 3.7'
|
|
28
26
|
end
|
|
@@ -14,6 +14,8 @@ module Grape
|
|
|
14
14
|
private
|
|
15
15
|
|
|
16
16
|
def serializable?(object)
|
|
17
|
+
return false if object.nil?
|
|
18
|
+
|
|
17
19
|
object.respond_to?(:serializable_hash) || object.respond_to?(:to_a) && object.all? { |o| o.respond_to? :serializable_hash } || object.is_a?(Hash)
|
|
18
20
|
end
|
|
19
21
|
|
|
@@ -21,7 +23,7 @@ module Grape
|
|
|
21
23
|
if object.respond_to? :serializable_hash
|
|
22
24
|
serializable_object(object, fast_jsonapi_options(env)).serializable_hash
|
|
23
25
|
elsif object.respond_to?(:to_a) && object.all? { |o| o.respond_to? :serializable_hash }
|
|
24
|
-
|
|
26
|
+
serializable_collection(object, fast_jsonapi_options(env))
|
|
25
27
|
elsif object.is_a?(Hash)
|
|
26
28
|
serialize_each_pair(object, env)
|
|
27
29
|
else
|
|
@@ -37,7 +39,18 @@ module Grape
|
|
|
37
39
|
serializable_class(object)&.new(object, options)
|
|
38
40
|
end
|
|
39
41
|
|
|
42
|
+
def serializable_collection(collection, options)
|
|
43
|
+
if collection.map(&:model_name).uniq.count > 1
|
|
44
|
+
collection.map do |o|
|
|
45
|
+
fast_jsonapi_serializable(o, options).serializable_hash || o.map(&:serializable_hash)
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
fast_jsonapi_serializable(collection, options).serializable_hash || collection.map(&:serializable_hash)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
40
52
|
def serializable_class(object)
|
|
53
|
+
object = object.first if object.is_a?(Array)
|
|
41
54
|
(object.model_name.name + 'Serializer').safe_constantize
|
|
42
55
|
end
|
|
43
56
|
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GrapeSwagger
|
|
4
|
+
module FastJsonapi
|
|
5
|
+
class Parser
|
|
6
|
+
attr_reader :model
|
|
7
|
+
attr_reader :endpoint
|
|
8
|
+
|
|
9
|
+
def initialize(model, endpoint)
|
|
10
|
+
@model = model
|
|
11
|
+
@endpoint = endpoint
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call
|
|
15
|
+
schema = default_schema
|
|
16
|
+
|
|
17
|
+
attributes_hash = if (defined? ActiveRecord)
|
|
18
|
+
map_active_record_columns_to_attributes
|
|
19
|
+
else
|
|
20
|
+
map_model_attributes
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
attributes_hash.each do |attribute, type|
|
|
24
|
+
schema[:data][:properties][:attributes][:properties][attribute] = { type: type }
|
|
25
|
+
schema[:data][:example][:attributes][attribute] = send "#{type}_example"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
relationships_hash = model.relationships_to_serialize || []
|
|
29
|
+
|
|
30
|
+
relationships_hash.each do |model_type, relationship_data|
|
|
31
|
+
relationships_attributes = relationship_data.instance_values.symbolize_keys
|
|
32
|
+
schema[:data][:properties][:relationships][:properties][model_type] = {
|
|
33
|
+
type: :object,
|
|
34
|
+
properties: relationships_properties(relationships_attributes)
|
|
35
|
+
}
|
|
36
|
+
schema[:data][:example][:relationships][model_type] = relationships_example(relationships_attributes)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
schema
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def default_schema
|
|
45
|
+
{
|
|
46
|
+
data: {
|
|
47
|
+
type: :object,
|
|
48
|
+
properties: {
|
|
49
|
+
id: { type: :integer },
|
|
50
|
+
type: { type: :string },
|
|
51
|
+
attributes: default_schema_object,
|
|
52
|
+
relationships: default_schema_object
|
|
53
|
+
},
|
|
54
|
+
example: {
|
|
55
|
+
id: 1,
|
|
56
|
+
type: model.record_type,
|
|
57
|
+
attributes: {},
|
|
58
|
+
relationships: {}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def default_schema_object
|
|
65
|
+
{ type: :object, properties: {} }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def map_active_record_columns_to_attributes
|
|
69
|
+
activerecord_model = model.record_type.to_s.camelize.safe_constantize
|
|
70
|
+
return map_model_attributes unless activerecord_model&.is_a?(ActiveRecord::Base)
|
|
71
|
+
|
|
72
|
+
columns = activerecord_model.columns.select do |c|
|
|
73
|
+
c.name.to_sym.in?(model.attributes_to_serialize.keys)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
attributes = {}
|
|
77
|
+
columns.each do |column|
|
|
78
|
+
attributes[column.name] = column.type
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
attributes
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def map_model_attributes
|
|
85
|
+
attributes = {}
|
|
86
|
+
model.attributes_to_serialize.each do |attribute, _|
|
|
87
|
+
attributes[attribute] = :string
|
|
88
|
+
end
|
|
89
|
+
attributes
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def relationships_properties(relationship_data)
|
|
93
|
+
if relationship_data[:relationship_type] == :has_many
|
|
94
|
+
{
|
|
95
|
+
data: {
|
|
96
|
+
type: :array,
|
|
97
|
+
items: relationship_default_item
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else
|
|
101
|
+
{
|
|
102
|
+
data: relationship_default_item
|
|
103
|
+
}
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def relationship_default_item
|
|
108
|
+
{
|
|
109
|
+
type: :object,
|
|
110
|
+
properties: {
|
|
111
|
+
id: { type: :integer },
|
|
112
|
+
type: { type: :string }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def relationships_example(relationship_data)
|
|
118
|
+
data = { id: 1, type: relationship_data[:record_type] }
|
|
119
|
+
if relationship_data[:relationship_type] == :has_many
|
|
120
|
+
{ data: [data] }
|
|
121
|
+
else
|
|
122
|
+
{ data: data }
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def integer_example
|
|
127
|
+
if defined? Faker
|
|
128
|
+
Faker::Number.number.to_i
|
|
129
|
+
else
|
|
130
|
+
rand(1..9999)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def string_example
|
|
135
|
+
if defined? Faker
|
|
136
|
+
Faker::Lorem.word
|
|
137
|
+
else
|
|
138
|
+
"Example string"
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def text_example
|
|
143
|
+
if defined? Faker
|
|
144
|
+
Faker::Lorem.paragraph
|
|
145
|
+
else
|
|
146
|
+
"Example string"
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def date_example
|
|
151
|
+
Date.today.to_s
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def datetime_example
|
|
155
|
+
Time.current.to_s
|
|
156
|
+
end
|
|
157
|
+
alias :time_example :datetime_example
|
|
158
|
+
|
|
159
|
+
def object_example
|
|
160
|
+
if defined? Faker
|
|
161
|
+
{
|
|
162
|
+
string_example.parameterize.underscore.to_sym => string_example.parameterize.underscore.to_sym
|
|
163
|
+
}
|
|
164
|
+
else
|
|
165
|
+
{ example: :object }
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def array_example
|
|
170
|
+
[string_example]
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def boolean_example
|
|
174
|
+
[true, false].sample
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
data/lib/grape_fast_jsonapi.rb
CHANGED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe Grape::Formatter::FastJsonapi do
|
|
4
|
+
describe 'class methods' do
|
|
5
|
+
let(:user) do
|
|
6
|
+
User.new(id: 1, first_name: 'Chuck', last_name: 'Norris', password: 'supersecretpassword', email: 'chuck@norris.com')
|
|
7
|
+
end
|
|
8
|
+
let(:another_user) do
|
|
9
|
+
User.new(id: 2, first_name: 'Bruce', last_name: 'Lee', password: 'supersecretpassword', email: 'bruce@lee.com')
|
|
10
|
+
end
|
|
11
|
+
let(:blog_post) do
|
|
12
|
+
BlogPost.new(id: 1, title: "Blog Post title", body: "Blog post body")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
describe '.call' do
|
|
16
|
+
subject { described_class.call(object, env) }
|
|
17
|
+
let(:env) { {} }
|
|
18
|
+
|
|
19
|
+
context 'when the object is a string' do
|
|
20
|
+
let(:object) { "I am a string" }
|
|
21
|
+
|
|
22
|
+
it { is_expected.to eq object }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'when the object is serializable' do
|
|
26
|
+
let(:user_serializer) { UserSerializer.new(object, {}) }
|
|
27
|
+
let(:blog_post_serializer) { BlogPostSerializer.new(object, {}) }
|
|
28
|
+
|
|
29
|
+
context 'when the object is a active serializable model instance' do
|
|
30
|
+
let(:object) { user }
|
|
31
|
+
|
|
32
|
+
it { is_expected.to eq ::Grape::Json.dump(user_serializer.serializable_hash) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context 'when the object is an array of active serializable model instances' do
|
|
36
|
+
let(:object) { [user, another_user] }
|
|
37
|
+
|
|
38
|
+
it { is_expected.to eq ::Grape::Json.dump(user_serializer.serializable_hash) }
|
|
39
|
+
|
|
40
|
+
context "when the array contains instances of different models" do
|
|
41
|
+
let(:object) { [user, blog_post] }
|
|
42
|
+
|
|
43
|
+
it 'returns an array of jsonapi serialialized objects' do
|
|
44
|
+
expect(subject).to eq(::Grape::Json.dump([
|
|
45
|
+
UserSerializer.new(user, {}).serializable_hash,
|
|
46
|
+
BlogPostSerializer.new(blog_post, {}).serializable_hash
|
|
47
|
+
]))
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context 'when the object is a Hash of plain values' do
|
|
53
|
+
let(:object) { user.as_json }
|
|
54
|
+
|
|
55
|
+
it { is_expected.to eq ::Grape::Json.dump(object) }
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context 'when the object is a Hash with serializable object values' do
|
|
59
|
+
let(:object) do
|
|
60
|
+
{
|
|
61
|
+
user: user,
|
|
62
|
+
blog_post: blog_post
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it 'returns an hash of with jsonapi serialialized objects values' do
|
|
67
|
+
expect(subject).to eq(::Grape::Json.dump({
|
|
68
|
+
user: UserSerializer.new(user, {}).serializable_hash,
|
|
69
|
+
blog_post: BlogPostSerializer.new(blog_post, {}).serializable_hash
|
|
70
|
+
}))
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context 'when the object is nil' do
|
|
75
|
+
let(:object) { nil }
|
|
76
|
+
|
|
77
|
+
it { is_expected.to eq 'null' }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
context 'when the object is a number' do
|
|
81
|
+
let(:object) { 42 }
|
|
82
|
+
|
|
83
|
+
it { is_expected.to eq '42' }
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
describe GrapeSwagger::FastJsonapi::Parser do
|
|
4
|
+
let(:model) { BlogPostSerializer }
|
|
5
|
+
let(:endpoint) { '/' }
|
|
6
|
+
|
|
7
|
+
describe 'attr_readers' do
|
|
8
|
+
subject { described_class.new(model, endpoint) }
|
|
9
|
+
|
|
10
|
+
it { expect(subject.model).to eq model }
|
|
11
|
+
it { expect(subject.endpoint).to eq endpoint }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
describe 'instance methods' do
|
|
15
|
+
describe '#call' do
|
|
16
|
+
subject { described_class.new(model, endpoint).call }
|
|
17
|
+
|
|
18
|
+
it 'return a hash defining the schema' do
|
|
19
|
+
expect(subject).to eq({
|
|
20
|
+
data: {
|
|
21
|
+
type: :object,
|
|
22
|
+
properties: {
|
|
23
|
+
id: { type: :integer },
|
|
24
|
+
type: { type: :string },
|
|
25
|
+
attributes: {
|
|
26
|
+
type: :object,
|
|
27
|
+
properties: {
|
|
28
|
+
title: { type: :string },
|
|
29
|
+
body: { type: :string }
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
relationships: {
|
|
33
|
+
type: :object,
|
|
34
|
+
properties: {
|
|
35
|
+
user: {
|
|
36
|
+
type: :object,
|
|
37
|
+
properties: {
|
|
38
|
+
data: {
|
|
39
|
+
type: :object,
|
|
40
|
+
properties: {
|
|
41
|
+
id: { type: :integer },
|
|
42
|
+
type: { type: :string }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
example: {
|
|
51
|
+
id: 1,
|
|
52
|
+
type: :blog_post,
|
|
53
|
+
attributes: {
|
|
54
|
+
title: "Example string",
|
|
55
|
+
body: "Example string",
|
|
56
|
+
},
|
|
57
|
+
relationships: {
|
|
58
|
+
user: {
|
|
59
|
+
data: {
|
|
60
|
+
id: 1,
|
|
61
|
+
type: :user
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context 'when the serializer contains sensitive information' do
|
|
71
|
+
let(:model) { UserSerializer } # contains :password attribute
|
|
72
|
+
|
|
73
|
+
it 'return a hash defining the schema filtering the sensitive attributes' do
|
|
74
|
+
expect(subject).to eq({
|
|
75
|
+
data: {
|
|
76
|
+
type: :object,
|
|
77
|
+
properties: {
|
|
78
|
+
id: { type: :integer },
|
|
79
|
+
type: { type: :string },
|
|
80
|
+
attributes: {
|
|
81
|
+
type: :object,
|
|
82
|
+
properties: {
|
|
83
|
+
first_name: { type: :string },
|
|
84
|
+
last_name: { type: :string },
|
|
85
|
+
email: { type: :string },
|
|
86
|
+
# password: { type: :string }, FILTERED
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
relationships: {
|
|
90
|
+
type: :object,
|
|
91
|
+
properties: {
|
|
92
|
+
blog_posts: {
|
|
93
|
+
type: :object,
|
|
94
|
+
properties: {
|
|
95
|
+
data: {
|
|
96
|
+
type: :array,
|
|
97
|
+
items: {
|
|
98
|
+
type: :object,
|
|
99
|
+
properties: {
|
|
100
|
+
id: { type: :integer },
|
|
101
|
+
type: { type: :string }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
example: {
|
|
111
|
+
id: 1,
|
|
112
|
+
type: :user,
|
|
113
|
+
attributes: {
|
|
114
|
+
first_name: "Example string",
|
|
115
|
+
last_name: "Example string",
|
|
116
|
+
email: "Example string",
|
|
117
|
+
# password: "Example string", FILTERED
|
|
118
|
+
},
|
|
119
|
+
relationships: {
|
|
120
|
+
blog_posts: {
|
|
121
|
+
data: [{ id: 1, type: :blog_post }]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class BlogPost
|
|
4
|
+
extend ActiveModel::Naming
|
|
5
|
+
include ActiveModel::Serialization
|
|
6
|
+
|
|
7
|
+
attr_accessor :id, :title, :body
|
|
8
|
+
|
|
9
|
+
def initialize(params = {})
|
|
10
|
+
params.each do |k, v|
|
|
11
|
+
instance_variable_set("@#{k}", v) unless v.nil?
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def user_id
|
|
16
|
+
nil
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class User
|
|
4
|
+
extend ActiveModel::Naming
|
|
5
|
+
include ActiveModel::Serialization
|
|
6
|
+
|
|
7
|
+
attr_accessor :id, :first_name, :last_name, :password, :email
|
|
8
|
+
|
|
9
|
+
def initialize(params = {})
|
|
10
|
+
params.each do |k, v|
|
|
11
|
+
instance_variable_set("@#{k}", v) unless v.nil?
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def attributes
|
|
16
|
+
{
|
|
17
|
+
'id' => nil,
|
|
18
|
+
'first_name' => nil,
|
|
19
|
+
'last_name' => nil,
|
|
20
|
+
'password' => nil,
|
|
21
|
+
'email' => nil
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def blog_post_ids
|
|
26
|
+
[]
|
|
27
|
+
end
|
|
28
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: grape_fast_jsonapi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emmanuel Cousin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2019-03-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: grape
|
|
@@ -30,70 +30,42 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '
|
|
33
|
+
version: '1.5'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '
|
|
40
|
+
version: '1.5'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rails
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version:
|
|
47
|
+
version: 4.2.0
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version:
|
|
54
|
+
version: 4.2.0
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: rspec
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
|
-
- - "
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
type: :development
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: rack-test
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - ">="
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '0'
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - ">="
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '0'
|
|
83
|
-
- !ruby/object:Gem::Dependency
|
|
84
|
-
name: rake
|
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
|
86
|
-
requirements:
|
|
87
|
-
- - ">="
|
|
59
|
+
- - "~>"
|
|
88
60
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '
|
|
61
|
+
version: '3.7'
|
|
90
62
|
type: :development
|
|
91
63
|
prerelease: false
|
|
92
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
65
|
requirements:
|
|
94
|
-
- - "
|
|
66
|
+
- - "~>"
|
|
95
67
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '
|
|
68
|
+
version: '3.7'
|
|
97
69
|
description: Provides a Formatter for the Grape API DSL to emit objects serialized
|
|
98
70
|
with fast_jsonapi.
|
|
99
71
|
email:
|
|
@@ -102,7 +74,11 @@ executables: []
|
|
|
102
74
|
extensions: []
|
|
103
75
|
extra_rdoc_files: []
|
|
104
76
|
files:
|
|
77
|
+
- ".circleci/config.yml"
|
|
78
|
+
- ".rspec"
|
|
79
|
+
- CHANGELOG.md
|
|
105
80
|
- Gemfile
|
|
81
|
+
- Gemfile.lock
|
|
106
82
|
- LICENSE
|
|
107
83
|
- README.md
|
|
108
84
|
- Rakefile
|
|
@@ -110,7 +86,16 @@ files:
|
|
|
110
86
|
- lib/grape_fast_jsonapi.rb
|
|
111
87
|
- lib/grape_fast_jsonapi/endpoint_extension.rb
|
|
112
88
|
- lib/grape_fast_jsonapi/formatter.rb
|
|
89
|
+
- lib/grape_fast_jsonapi/parser.rb
|
|
113
90
|
- lib/grape_fast_jsonapi/version.rb
|
|
91
|
+
- spec/lib/grape_fast_jsonapi/formatter_spec.rb
|
|
92
|
+
- spec/lib/grape_fast_jsonapi/parser_spec.rb
|
|
93
|
+
- spec/lib/grape_fast_jsonapi/version_spec.rb
|
|
94
|
+
- spec/spec_helper.rb
|
|
95
|
+
- spec/support/models/blog_post.rb
|
|
96
|
+
- spec/support/models/user.rb
|
|
97
|
+
- spec/support/serializers/blog_post_serializer.rb
|
|
98
|
+
- spec/support/serializers/user_serializer.rb
|
|
114
99
|
homepage: https://github.com/EmCousin/grape_fast_jsonapi
|
|
115
100
|
licenses:
|
|
116
101
|
- MIT
|
|
@@ -130,9 +115,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
130
115
|
- !ruby/object:Gem::Version
|
|
131
116
|
version: '0'
|
|
132
117
|
requirements: []
|
|
133
|
-
|
|
134
|
-
rubygems_version: 2.5.2
|
|
118
|
+
rubygems_version: 3.0.1
|
|
135
119
|
signing_key:
|
|
136
120
|
specification_version: 4
|
|
137
121
|
summary: Use fast_jsonapi in grape
|
|
138
|
-
test_files:
|
|
122
|
+
test_files:
|
|
123
|
+
- spec/lib/grape_fast_jsonapi/formatter_spec.rb
|
|
124
|
+
- spec/lib/grape_fast_jsonapi/parser_spec.rb
|
|
125
|
+
- spec/lib/grape_fast_jsonapi/version_spec.rb
|
|
126
|
+
- spec/spec_helper.rb
|
|
127
|
+
- spec/support/models/blog_post.rb
|
|
128
|
+
- spec/support/models/user.rb
|
|
129
|
+
- spec/support/serializers/blog_post_serializer.rb
|
|
130
|
+
- spec/support/serializers/user_serializer.rb
|