jsonapi_for_rails 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +229 -0
- data/Rakefile +34 -0
- data/lib/jsonapi_for_rails/controller/actions/object.rb +293 -0
- data/lib/jsonapi_for_rails/controller/actions/relationship.rb +111 -0
- data/lib/jsonapi_for_rails/controller/before_actions/include.rb +30 -0
- data/lib/jsonapi_for_rails/controller/before_actions/record.rb +51 -0
- data/lib/jsonapi_for_rails/controller/before_actions/relationship.rb +34 -0
- data/lib/jsonapi_for_rails/controller/before_actions/sparse_fieldsets.rb +39 -0
- data/lib/jsonapi_for_rails/controller/utils/model.rb +36 -0
- data/lib/jsonapi_for_rails/controller/utils/render.rb +59 -0
- data/lib/jsonapi_for_rails/controller.rb +33 -0
- data/lib/jsonapi_for_rails/model.rb +94 -0
- data/lib/jsonapi_for_rails/version.rb +3 -0
- data/lib/jsonapi_for_rails.rb +15 -0
- data/lib/tasks/jsonapi_for_rails_tasks.rake +4 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f9cfc12b29fbd2b7976e2f8afdd25efd52b8f1e3
|
4
|
+
data.tar.gz: 4a677ebb54f964c8e339b56dfe2c03d886f669b9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9ded9261047e1a2f118251b7003ee0113722c3ef9086d9228d8d1a4943c75633ea509e91519f987c6ea93e664e86beadc55c53c8f86e61bf1b9af2546f6f9628
|
7
|
+
data.tar.gz: b5b734f5214dbde8371c756974a335e92deba05bf18888687b8a7701eac471b16e99190c863151022fe7b7f46ebbf5634b6d8ed056824b2469cf2c84c4b5b06a
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2016 Doga Armangil
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,229 @@
|
|
1
|
+
# JsonapiForRails
|
2
|
+
A [Rails](http://rubyonrails.org/) 5+ plugin for providing a [JSONAPI v1.0](http://jsonapi.org/format/1.0/) API from your application with very little coding.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
|
6
|
+
### 1. Set up one API controller per model
|
7
|
+
|
8
|
+
Generate a controller for each model that will be accessible from your API. Controller names need to be the plural version of your model names.
|
9
|
+
|
10
|
+
```bash
|
11
|
+
$ # Go to the root directory of your existing Rails application
|
12
|
+
$ cd path/to/railsapp
|
13
|
+
$
|
14
|
+
$ # Generate your models
|
15
|
+
$ bin/rails generate model author
|
16
|
+
$ bin/rails generate model article
|
17
|
+
$
|
18
|
+
$ # Generate your API controllers
|
19
|
+
$ bin/rails generate controller authors
|
20
|
+
$ bin/rails generate controller articles
|
21
|
+
```
|
22
|
+
|
23
|
+
Then enable JSONAPI in a parent class of your API controllers.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
# app/controllers/application_controller.rb
|
27
|
+
class ApplicationController < ActionController::Base # or ActionController::API
|
28
|
+
|
29
|
+
# Enable JSONAPI
|
30
|
+
acts_as_jsonapi_resources
|
31
|
+
|
32
|
+
# ...
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
If only some of your controllers are JSONAPI controllers, then create a parent controller for only those controllers, and enable JSONAPI inside that controller rather than `ApplicationController`.
|
37
|
+
|
38
|
+
```bash
|
39
|
+
$ cat > app/controllers/jsonapi_resources_controller.rb
|
40
|
+
|
41
|
+
class JsonapiResourcesController < ApplicationController
|
42
|
+
acts_as_jsonapi_resources
|
43
|
+
|
44
|
+
# ...
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
# app/controllers/authors_controller.rb
|
50
|
+
|
51
|
+
# Change the API controller's parent class
|
52
|
+
class AuthorsController < JsonapiResourcesController
|
53
|
+
# ...
|
54
|
+
end
|
55
|
+
|
56
|
+
# Do the same with ArticlesController
|
57
|
+
```
|
58
|
+
|
59
|
+
### 2. Configure your API controller routes
|
60
|
+
Update your application routes as follows:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
# config/routes.rb
|
64
|
+
Rails.application.routes.draw do
|
65
|
+
# ...
|
66
|
+
|
67
|
+
scope '/api/v1' do # Optional scoping
|
68
|
+
|
69
|
+
[ # List your API controllers here
|
70
|
+
:authors, :articles
|
71
|
+
].each do |resources_name|
|
72
|
+
resources resources_name do
|
73
|
+
controller resources_name do
|
74
|
+
get 'relationships/:relationship', action: "relationship_show"
|
75
|
+
patch 'relationships/:relationship', action: "relationship_update"
|
76
|
+
post 'relationships/:relationship', action: "relationship_add"
|
77
|
+
delete 'relationships/:relationship', action: "relationship_remove"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
# ...
|
85
|
+
end
|
86
|
+
|
87
|
+
```
|
88
|
+
|
89
|
+
### 3. Verify your setup
|
90
|
+
After populating your database and launching the built-in Rails server with the `bin/rails server` shell command, you can issue some HTTP requests to your API and verify the correctness of the responses.
|
91
|
+
|
92
|
+
```bash
|
93
|
+
$ # Get the list of articles
|
94
|
+
$ curl 'http://localhost:3000/api/v1/articles'
|
95
|
+
{"data":[{"type":"articles","id":184578894},{"type":"articles","id":388548390},{"type":"articles","id":618037523},{"type":"articles","id":994552601}]}
|
96
|
+
$
|
97
|
+
$ # Get an article
|
98
|
+
$ curl 'http://localhost:3000/api/v1/articles/184578894'
|
99
|
+
{"data":{"type":"articles","id":618037523,"attributes":{"title":"UK bank pay and bonuses in the spotlight as results season starts","content":"The pay deals handed to the bosses of Britain’s biggest banks will be in focus ...","created_at":"2016-02-22T16:57:43.401Z","updated_at":"2016-02-22T16:57:43.401Z"},"relationships":{"author":{"data":{"type":"authors","id":1023487079}}}}}
|
100
|
+
$
|
101
|
+
$ # Get only the title of an article, include the author name
|
102
|
+
$ curl 'http://localhost:3000/api/v1/articles/184578894?filter%5Barticles%5D=title,author;include=author;filter%5Bauthors%5D=name'
|
103
|
+
{"data":{"type":"articles","id":618037523,"attributes":{"title":"UK bank pay and bonuses in the spotlight as results season starts"},"relationships":{"author":{"data":{"type":"authors","id":1023487079}}}},"include":[{"data":{"type":"authors","id":1023487079,"attributes":{"name":"..."},"relationships":{}}}]}
|
104
|
+
|
105
|
+
```
|
106
|
+
|
107
|
+
## Modifying the default API behaviour
|
108
|
+
By default, all API end-points are accessible to all clients, and all end-points behave the same way for all clients. In a real-world setting, you may want to restrict access to an end-point and/or change the behaviour of an end-point depending on the client.
|
109
|
+
|
110
|
+
### Client authentication
|
111
|
+
Clients can be authenticated with a `before_action` method in your API controller. Inside controllers, instance variable names starting with the `@jsonapi_` prefix and method names starting with the `jsonapi_` prefix are reserved by *jsonapi_for_rails*, so try to avoid those.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
# app/controllers/jsonapi_resources_controller.rb
|
115
|
+
class JsonapiResourcesController < ApplicationController
|
116
|
+
acts_as_jsonapi_resources
|
117
|
+
|
118
|
+
before_action :authenticate
|
119
|
+
|
120
|
+
private
|
121
|
+
def authenticate
|
122
|
+
# ...
|
123
|
+
end
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
127
|
+
### Access control
|
128
|
+
Access control for authenticated and unauthenticated clients can be implemented in `before_action` methods in your API controllers.
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
# app/controllers/jsonapi_resources_controller.rb
|
132
|
+
class JsonapiResourcesController < ApplicationController
|
133
|
+
acts_as_jsonapi_resources
|
134
|
+
|
135
|
+
before_action :permit_read, only: [
|
136
|
+
:index,
|
137
|
+
:show,
|
138
|
+
:relationship_show
|
139
|
+
]
|
140
|
+
|
141
|
+
before_action :permit_write, only: [
|
142
|
+
:create,
|
143
|
+
:update,
|
144
|
+
:destroy,
|
145
|
+
:relationship_update,
|
146
|
+
:relationship_add,
|
147
|
+
:relationship_remove
|
148
|
+
]
|
149
|
+
|
150
|
+
private
|
151
|
+
def permit_read
|
152
|
+
# ...
|
153
|
+
end
|
154
|
+
|
155
|
+
def permit_write
|
156
|
+
# ...
|
157
|
+
end
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
### Overriding an API end-point
|
162
|
+
The `bin/rails routes` shell command shows you the end-points that *jsonapi_for_rails* defines. In order to change the behaviour of an action, you can define an action with the same name inside an API controller. *jsonapi_for_rails* provides utility methods and instance variables that can help you.
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
# app/controllers/articles_controller.rb
|
166
|
+
class ArticlesController < JsonapiResourcesController
|
167
|
+
|
168
|
+
def index
|
169
|
+
jsonapi_model_class # => Article
|
170
|
+
jsonapi_model_class_name # => "Article"
|
171
|
+
jsonapi_model_type # => :articles
|
172
|
+
|
173
|
+
# ...
|
174
|
+
end
|
175
|
+
|
176
|
+
def show
|
177
|
+
@jsonapi_record.to_jsonapi_hash # => {data: {...}}
|
178
|
+
|
179
|
+
# ...
|
180
|
+
end
|
181
|
+
|
182
|
+
def relationship_show
|
183
|
+
@jsonapi_relationship # => {:definition=>{:name=>:author, :type=>:to_one, :receiver=>{:type=>:authors, :class=>Author}}
|
184
|
+
|
185
|
+
# ...
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
```
|
190
|
+
|
191
|
+
## Implementation status
|
192
|
+
* [Inclusion of related resources](http://jsonapi.org/format/1.0/#fetching-includes) is currently only implemented for requests that return a single resource, and relationship paths are not supported.
|
193
|
+
* [Sparse fieldsets](http://jsonapi.org/format/1.0/#fetching-sparse-fieldsets) is currently only implemented for requests that return a single resource.
|
194
|
+
* [Sorting](http://jsonapi.org/format/1.0/#fetching-sorting) is currently not implemented.
|
195
|
+
* [Pagination](http://jsonapi.org/format/1.0/#fetching-pagination) is currently not implemented.
|
196
|
+
* [Deleting resources](http://jsonapi.org/format/1.0/#crud-deleting) is currently not implemented.
|
197
|
+
* Test coverage is sparse.
|
198
|
+
|
199
|
+
## Installation
|
200
|
+
|
201
|
+
### Edge version
|
202
|
+
|
203
|
+
```bash
|
204
|
+
$ # Clone this repository
|
205
|
+
$ git clone 'https://github.com/doga/jsonapi_for_rails.git'
|
206
|
+
$
|
207
|
+
$ # Update your Rails application's gem file
|
208
|
+
$ cat >> path/to/Gemfile
|
209
|
+
|
210
|
+
group :development do
|
211
|
+
gem 'jsonapi_for_rails', path: 'path/to/jsonapi_for_rails'
|
212
|
+
#gem 'jsonapi_for_rails', git: 'https://github.com/doga/jsonapi_for_rails.git'
|
213
|
+
end
|
214
|
+
```
|
215
|
+
|
216
|
+
### Latest stable version
|
217
|
+
|
218
|
+
```bash
|
219
|
+
$ # Update your Rails application's gem file
|
220
|
+
$ cat >> path/to/Gemfile
|
221
|
+
|
222
|
+
gem 'jsonapi_for_rails'
|
223
|
+
```
|
224
|
+
|
225
|
+
## Contributing
|
226
|
+
If you find a bug in this project, have trouble following the documentation or have a question about the project – create an [issue](https://guides.github.com/activities/contributing-to-open-source/#contributing).
|
227
|
+
|
228
|
+
## License
|
229
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'JsonapiForRails'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
Bundler::GemHelper.install_tasks
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,293 @@
|
|
1
|
+
module JsonapiForRails::Controller
|
2
|
+
|
3
|
+
module Actions
|
4
|
+
module Object
|
5
|
+
def self.included receiver
|
6
|
+
receiver.send :include, InstanceMethods
|
7
|
+
run_macros receiver
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
# TODO: pagination
|
12
|
+
def index
|
13
|
+
@json = {data: []}
|
14
|
+
jsonapi_model_class.all.each do |record|
|
15
|
+
@json[:data] << {
|
16
|
+
type: record.class.to_s.underscore.pluralize, # TODO: factor out type generation from class
|
17
|
+
id: record.id
|
18
|
+
}
|
19
|
+
end
|
20
|
+
render_json @json
|
21
|
+
end
|
22
|
+
|
23
|
+
# implements Create and Update operations
|
24
|
+
def create
|
25
|
+
begin
|
26
|
+
# attributes
|
27
|
+
attrs = received_attributes
|
28
|
+
if attrs
|
29
|
+
if @jsonapi_record
|
30
|
+
# update
|
31
|
+
@jsonapi_record.update! attrs
|
32
|
+
else
|
33
|
+
# create
|
34
|
+
@jsonapi_record = jsonapi_model_class.create! attrs
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# relationships
|
39
|
+
received_relationships.each do |relationship|
|
40
|
+
# to-one
|
41
|
+
if relationship[:definition][:type] == :to_one
|
42
|
+
@jsonapi_record.send :"#{relationship[:definition][:name]}=", relationship[:params][:data]
|
43
|
+
next
|
44
|
+
end
|
45
|
+
|
46
|
+
# to-many
|
47
|
+
@jsonapi_record.send(relationship[:definition][:name]).send :clear # initialize the relation
|
48
|
+
|
49
|
+
relationship[:params][:data].each do |item|
|
50
|
+
object = relationship[:receiver][:class].find_by_id item[:id]
|
51
|
+
@jsonapi_record.send(relationship[:definition][:name]).send :<<, object
|
52
|
+
end
|
53
|
+
end
|
54
|
+
rescue NameError => e
|
55
|
+
|
56
|
+
# error when creating record
|
57
|
+
render_error 500, "Model class not found."
|
58
|
+
return
|
59
|
+
rescue
|
60
|
+
# error when creating relationship?
|
61
|
+
@jsonapi_record.destroy if @jsonapi_record
|
62
|
+
|
63
|
+
render_error 500, "Record could not be created."
|
64
|
+
return
|
65
|
+
end
|
66
|
+
show
|
67
|
+
end
|
68
|
+
|
69
|
+
def show
|
70
|
+
@json = @jsonapi_record.to_jsonapi_hash(
|
71
|
+
@jsonapi_sparse_fieldsets[jsonapi_model_type]
|
72
|
+
)
|
73
|
+
#$stderr.puts "#{@json}"
|
74
|
+
|
75
|
+
# Include resources
|
76
|
+
# TODO: relationship paths when including resources (http://jsonapi.org/format/1.0/#fetching-includes)
|
77
|
+
if @jsonapi_include and @json[:data][:relationships]
|
78
|
+
@json[:include] = []
|
79
|
+
@jsonapi_include.each do |rel_name|
|
80
|
+
rel = @json[:data][:relationships][rel_name]
|
81
|
+
next unless rel
|
82
|
+
rel = rel[:data]
|
83
|
+
next unless rel
|
84
|
+
rel = [rel] if rel.kind_of?(Hash)
|
85
|
+
rel.each do |r|
|
86
|
+
type = r[:type].to_sym
|
87
|
+
klass = nil
|
88
|
+
begin
|
89
|
+
klass = r[:type].singularize.camelize.constantize
|
90
|
+
rescue NameError => e
|
91
|
+
next
|
92
|
+
end
|
93
|
+
r = klass.find_by_id r[:id]
|
94
|
+
next unless r
|
95
|
+
|
96
|
+
@json[:include] << r.to_jsonapi_hash(
|
97
|
+
@jsonapi_sparse_fieldsets[type]
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
render_json @json
|
104
|
+
end
|
105
|
+
|
106
|
+
def update
|
107
|
+
create
|
108
|
+
end
|
109
|
+
|
110
|
+
def destroy
|
111
|
+
render_error 500, "Not implemented."
|
112
|
+
end
|
113
|
+
|
114
|
+
# private
|
115
|
+
|
116
|
+
# Extracts record attributes from received params.
|
117
|
+
# Use this for creating/updating a database record.
|
118
|
+
# Note that relationships (has_one associations etc) are filtered out
|
119
|
+
# but are still available in the original params.
|
120
|
+
def received_attributes
|
121
|
+
begin
|
122
|
+
params.require(
|
123
|
+
:data
|
124
|
+
).require(
|
125
|
+
:attributes
|
126
|
+
).permit(
|
127
|
+
*jsonapi_model_class.attribute_names
|
128
|
+
).reject do |key, value|
|
129
|
+
# ignore automatically generated attributes
|
130
|
+
%w(
|
131
|
+
id
|
132
|
+
created_at created_on
|
133
|
+
updated_at updated_on
|
134
|
+
).include?(
|
135
|
+
key.to_s
|
136
|
+
) or
|
137
|
+
|
138
|
+
# ignore reference attributes
|
139
|
+
key.to_s =~ /_id$/
|
140
|
+
end
|
141
|
+
rescue ActionController::ParameterMissing => e
|
142
|
+
nil
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def relationships
|
147
|
+
jsonapi_model_class.reflect_on_all_associations.collect do |association|
|
148
|
+
type = nil
|
149
|
+
|
150
|
+
type = :to_one if [
|
151
|
+
ActiveRecord::Reflection::HasOneReflection,
|
152
|
+
ActiveRecord::Reflection::BelongsToReflection
|
153
|
+
].include? association.class
|
154
|
+
|
155
|
+
type = :to_many if [
|
156
|
+
ActiveRecord::Reflection::HasManyReflection,
|
157
|
+
ActiveRecord::Reflection::HasAndBelongsToManyReflection
|
158
|
+
].include? association.class
|
159
|
+
|
160
|
+
next unless type
|
161
|
+
|
162
|
+
{
|
163
|
+
name: association.name,
|
164
|
+
type: type,
|
165
|
+
receiver: {
|
166
|
+
type: association.klass.to_s.underscore.pluralize.to_sym,
|
167
|
+
class: association.klass.to_s.constantize
|
168
|
+
}
|
169
|
+
}
|
170
|
+
end.compact
|
171
|
+
end
|
172
|
+
|
173
|
+
def received_relationships
|
174
|
+
rels = relationships
|
175
|
+
if params[:relationship] # only one relationship received for relationship action
|
176
|
+
rels.select! do |rel|
|
177
|
+
rel[:name].to_sym == params[:relationship].to_sym
|
178
|
+
end
|
179
|
+
if request.method == "GET"
|
180
|
+
# no relationship received, return definition only
|
181
|
+
return rels.collect do |rel|
|
182
|
+
{definition: rel}
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
rels.collect do |relationship|
|
187
|
+
begin
|
188
|
+
received_params = nil
|
189
|
+
if params[:relationship]
|
190
|
+
received_params = params.permit({
|
191
|
+
data: [
|
192
|
+
:type, :id
|
193
|
+
]
|
194
|
+
})
|
195
|
+
else
|
196
|
+
received_params = params.require(
|
197
|
+
:data
|
198
|
+
).require(
|
199
|
+
:relationships
|
200
|
+
).require(
|
201
|
+
relationship[:name]
|
202
|
+
).permit({
|
203
|
+
data: [
|
204
|
+
:type, :id
|
205
|
+
]
|
206
|
+
})
|
207
|
+
end
|
208
|
+
# => {"data"=>{"type"=>"users", "id"=>1}} # sample value for a to-one association
|
209
|
+
# => {"data"=>[{"type"=>"properties", "id"=>1}, {"type"=>"properties", "id"=>2}]} # sample value for a to-many association
|
210
|
+
|
211
|
+
# is received data conformant to the database schema?
|
212
|
+
conformant = true
|
213
|
+
loop do
|
214
|
+
# to-many
|
215
|
+
if received_params[:data].kind_of? Array
|
216
|
+
if relationship[:type] != :to_many
|
217
|
+
conformant = false
|
218
|
+
break
|
219
|
+
end
|
220
|
+
received_params[:data].each do |item|
|
221
|
+
next if item[:type] == relationship[:receiver][:type]
|
222
|
+
conformant = false
|
223
|
+
break
|
224
|
+
end
|
225
|
+
break
|
226
|
+
end
|
227
|
+
|
228
|
+
# to-one
|
229
|
+
if relationship[:type] != :to_one
|
230
|
+
conformant = false
|
231
|
+
break
|
232
|
+
end
|
233
|
+
conformant = false unless received_params[:data][:type] == relationship[:receiver][:type]
|
234
|
+
|
235
|
+
break
|
236
|
+
end
|
237
|
+
next unless conformant
|
238
|
+
|
239
|
+
{
|
240
|
+
definition: relationship,
|
241
|
+
params: received_params
|
242
|
+
}
|
243
|
+
rescue ActionController::ParameterMissing => e
|
244
|
+
|
245
|
+
# nil assignment to to-one relationship?
|
246
|
+
if relationship[:type] == :to_one
|
247
|
+
begin
|
248
|
+
if params[:relationship] # relationship action
|
249
|
+
received_params = params.permit(
|
250
|
+
:data
|
251
|
+
)
|
252
|
+
else
|
253
|
+
received_params = params.require(
|
254
|
+
:data
|
255
|
+
).require(
|
256
|
+
:relationships
|
257
|
+
).require(
|
258
|
+
relationship[:name]
|
259
|
+
).permit(
|
260
|
+
:data
|
261
|
+
)
|
262
|
+
end
|
263
|
+
|
264
|
+
# received nil?
|
265
|
+
next if received_params[:data] # TODO: should return error to client?
|
266
|
+
|
267
|
+
next {
|
268
|
+
definition: relationship,
|
269
|
+
params: received_params
|
270
|
+
}
|
271
|
+
rescue ActionController::ParameterMissing => e
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
nil
|
276
|
+
end
|
277
|
+
end.compact
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
|
282
|
+
def self.run_macros receiver
|
283
|
+
receiver.instance_exec do
|
284
|
+
private :received_attributes
|
285
|
+
private :relationships
|
286
|
+
private :received_relationships
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module JsonapiForRails::Controller
|
2
|
+
|
3
|
+
module Actions
|
4
|
+
module Relationship
|
5
|
+
def self.included receiver
|
6
|
+
receiver.send :include, InstanceMethods
|
7
|
+
run_macros receiver
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
# GET
|
12
|
+
def relationship_show
|
13
|
+
#$stderr.puts "JsonapiForRails::Controller::Actions::Relationship#relationship_show called"
|
14
|
+
rel = @jsonapi_record.send @jsonapi_relationship[:definition][:name]
|
15
|
+
|
16
|
+
@json = nil
|
17
|
+
if @jsonapi_relationship[:definition][:type] == :to_one
|
18
|
+
@json = {
|
19
|
+
type: @jsonapi_relationship[:definition][:receiver][:type],
|
20
|
+
id: rel.id
|
21
|
+
}
|
22
|
+
elsif @jsonapi_relationship[:definition][:type] == :to_many
|
23
|
+
@json = rel.collect do |r|
|
24
|
+
{
|
25
|
+
type: @jsonapi_relationship[:definition][:receiver][:type],
|
26
|
+
id: r.id
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
@json = {data: @json}
|
31
|
+
|
32
|
+
render_json @json
|
33
|
+
end
|
34
|
+
|
35
|
+
# PATCH
|
36
|
+
def relationship_update
|
37
|
+
if @jsonapi_relationship[:definition][:type] == :to_many
|
38
|
+
render_error 403, 'Replacing all members of a to-many relationship is forbidden.'
|
39
|
+
return
|
40
|
+
end
|
41
|
+
|
42
|
+
related = nil
|
43
|
+
if @jsonapi_relationship[:params][:data]
|
44
|
+
related = @jsonapi_relationship[:definition][:receiver][:class].find_by_id(
|
45
|
+
@jsonapi_relationship[:params][:data][:id]
|
46
|
+
)
|
47
|
+
unless related
|
48
|
+
render_error 403, 'Record not found.'
|
49
|
+
return
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
@jsonapi_record.send :"#{@jsonapi_relationship[:definition][:name]}=", related
|
54
|
+
@jsonapi_record.save
|
55
|
+
end
|
56
|
+
|
57
|
+
# POST for to-many relations only
|
58
|
+
def relationship_add
|
59
|
+
unless @jsonapi_relationship[:definition][:type] == :to_many
|
60
|
+
render_error 403, 'Operation allowed for to-many relationships only.'
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
64
|
+
records = @jsonapi_relationship[:params][:data].collect do |record|
|
65
|
+
record = @jsonapi_relationship[:definition][:receiver][:class].find_by_id(
|
66
|
+
record[:id]
|
67
|
+
)
|
68
|
+
unless record
|
69
|
+
render_error 403, "Non-existing record #{record.inspect}."
|
70
|
+
return
|
71
|
+
end
|
72
|
+
record
|
73
|
+
end
|
74
|
+
|
75
|
+
records.each do |record|
|
76
|
+
@jsonapi_record.send(@jsonapi_relationship[:definition][:name]) << record
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# DELETE for to-many relations only
|
81
|
+
def relationship_remove
|
82
|
+
unless @jsonapi_relationship[:definition][:type] == :to_many
|
83
|
+
render_error 403, 'Operation allowed for to-many relationships only.'
|
84
|
+
return
|
85
|
+
end
|
86
|
+
|
87
|
+
records = @jsonapi_relationship[:params][:data].collect do |record|
|
88
|
+
record = @jsonapi_relationship[:definition][:receiver][:class].find_by_id(
|
89
|
+
record[:id]
|
90
|
+
)
|
91
|
+
unless record
|
92
|
+
render_error 403, "Non-existing record #{record.inspect}."
|
93
|
+
return
|
94
|
+
end
|
95
|
+
record
|
96
|
+
end
|
97
|
+
|
98
|
+
records.each do |record|
|
99
|
+
@jsonapi_record.send(@jsonapi_relationship[:definition][:name]).delete record
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.run_macros receiver
|
105
|
+
receiver.instance_exec do
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module JsonapiForRails::Controller
|
2
|
+
|
3
|
+
module BeforeActions
|
4
|
+
module Include
|
5
|
+
|
6
|
+
def self.included receiver
|
7
|
+
receiver.send :include, InstanceMethods
|
8
|
+
run_macros receiver
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.run_macros receiver
|
12
|
+
receiver.instance_exec do
|
13
|
+
before_action :jsonapi_include
|
14
|
+
private :jsonapi_include
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module InstanceMethods
|
19
|
+
def jsonapi_include
|
20
|
+
#@jsonapi_include = nil
|
21
|
+
return unless params[:include]
|
22
|
+
|
23
|
+
@jsonapi_include = params[:include].split(',').map{|rel| rel.strip.to_sym }
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module JsonapiForRails::Controller
|
2
|
+
|
3
|
+
module BeforeActions
|
4
|
+
module Record
|
5
|
+
|
6
|
+
def self.included receiver
|
7
|
+
#$stderr.puts "JsonapiForRails::Controller::RecordFromRequest included into #{receiver}"
|
8
|
+
receiver.send :include, InstanceMethods
|
9
|
+
run_macros receiver
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.run_macros receiver
|
13
|
+
receiver.instance_exec do
|
14
|
+
before_action :jsonapi_require_record, except: [
|
15
|
+
:index,
|
16
|
+
:create
|
17
|
+
]
|
18
|
+
private :jsonapi_require_record
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
module InstanceMethods
|
23
|
+
def jsonapi_require_record
|
24
|
+
#$stderr.puts "@jsonapi_sparse_fieldsets: #{@jsonapi_sparse_fieldsets.inspect}"
|
25
|
+
#$stderr.puts "JsonapiForRails::Controller::RecordFromRequest#jsonapi_require_record called"
|
26
|
+
if params[:relationship]
|
27
|
+
# relationship action
|
28
|
+
@jsonapi_record = jsonapi_model_class.find_by_id params["#{jsonapi_model_class_name.underscore}_id"].to_i
|
29
|
+
else
|
30
|
+
# CRUD action
|
31
|
+
@jsonapi_record = jsonapi_model_class
|
32
|
+
=begin
|
33
|
+
if false and @jsonapi_sparse_fieldsets[jsonapi_model_type]
|
34
|
+
@jsonapi_record = @jsonapi_record.select(
|
35
|
+
@jsonapi_sparse_fieldsets[jsonapi_model_type]
|
36
|
+
)
|
37
|
+
end
|
38
|
+
=end
|
39
|
+
@jsonapi_record = @jsonapi_record.find_by_id params[:id].to_i
|
40
|
+
end
|
41
|
+
#$stderr.puts "@jsonapi_record: #{@jsonapi_record.inspect}"
|
42
|
+
return if @jsonapi_record
|
43
|
+
|
44
|
+
render_error 401, "Bad request."
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module JsonapiForRails::Controller
|
2
|
+
|
3
|
+
module BeforeActions
|
4
|
+
module Relationship
|
5
|
+
|
6
|
+
def self.included receiver
|
7
|
+
receiver.send :include, InstanceMethods
|
8
|
+
run_macros receiver
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.run_macros receiver
|
12
|
+
receiver.instance_exec do
|
13
|
+
before_action :jsonapi_require_relationship, only: [
|
14
|
+
:relationship_show,
|
15
|
+
:relationship_update,
|
16
|
+
:relationship_add,
|
17
|
+
:relationship_remove
|
18
|
+
]
|
19
|
+
private :jsonapi_require_relationship
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
def jsonapi_require_relationship
|
25
|
+
#$stderr.puts "JsonapiForRails::Controller::RelationshipFromRequest#jsonapi_require_relationship called"
|
26
|
+
@jsonapi_relationship = received_relationships.first
|
27
|
+
return if @jsonapi_relationship
|
28
|
+
|
29
|
+
render_error 401, "Bad request."
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module JsonapiForRails::Controller
|
2
|
+
|
3
|
+
module BeforeActions
|
4
|
+
module SparseFieldsets
|
5
|
+
|
6
|
+
def self.included receiver
|
7
|
+
receiver.send :include, InstanceMethods
|
8
|
+
run_macros receiver
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.run_macros receiver
|
12
|
+
receiver.instance_exec do
|
13
|
+
before_action :jsonapi_sparse_fieldsets
|
14
|
+
private :jsonapi_sparse_fieldsets
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module InstanceMethods
|
19
|
+
def jsonapi_sparse_fieldsets
|
20
|
+
@jsonapi_sparse_fieldsets = {}
|
21
|
+
return unless params[:fields]
|
22
|
+
|
23
|
+
params[:fields].each do |resources_name, fields|
|
24
|
+
resources_name = resources_name.to_sym
|
25
|
+
fields =
|
26
|
+
fields.split(',').
|
27
|
+
map{|field| field.strip.to_sym }.
|
28
|
+
select{|e| e =~ /^[A-Za-z1-9_]+$/} # BUG: selector too restrictive
|
29
|
+
next if fields.size.zero?
|
30
|
+
@jsonapi_sparse_fieldsets[resources_name] = fields#.join(',')
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module JsonapiForRails::Controller
|
2
|
+
|
3
|
+
module Utils
|
4
|
+
module Model
|
5
|
+
def self.included receiver
|
6
|
+
#$stderr.puts "JsonapiForRails::Controller::ModelUtils included into #{receiver}"
|
7
|
+
receiver.send :include, InstanceMethods
|
8
|
+
run_macros receiver
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
def jsonapi_model_class_name
|
13
|
+
controller_class_name = "#{self.class}"
|
14
|
+
controller_class_name.underscore.split('_')[0..-2].join('_').camelize.singularize
|
15
|
+
end
|
16
|
+
|
17
|
+
def jsonapi_model_class
|
18
|
+
jsonapi_model_class_name.constantize # Object.const_get jsonapi_model_class_name
|
19
|
+
end
|
20
|
+
|
21
|
+
# used in returned JSON API data
|
22
|
+
def jsonapi_model_type
|
23
|
+
jsonapi_model_class_name.underscore.pluralize.to_sym
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.run_macros receiver
|
28
|
+
receiver.instance_exec do
|
29
|
+
private :jsonapi_model_class_name, :jsonapi_model_class, :jsonapi_model_type
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module JsonapiForRails::Controller
|
2
|
+
|
3
|
+
module Utils
|
4
|
+
module Render
|
5
|
+
def self.included receiver
|
6
|
+
receiver.send :include, InstanceMethods
|
7
|
+
run_macros receiver
|
8
|
+
end
|
9
|
+
|
10
|
+
JSONAPI = {
|
11
|
+
specification: 'http://jsonapi.org/format/',
|
12
|
+
content_type: 'application/vnd.api+json'
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def self.run_macros receiver
|
16
|
+
receiver.instance_exec do
|
17
|
+
private :render_json, :render_error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module InstanceMethods
|
22
|
+
def render_json object
|
23
|
+
unless object
|
24
|
+
render_error 500, 'No message specified.'
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
@status = 200
|
29
|
+
@json = object
|
30
|
+
@content_type = JSONAPI[:content_type]
|
31
|
+
|
32
|
+
render(
|
33
|
+
json: @json,
|
34
|
+
status: @status,
|
35
|
+
content_type: @content_type
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def render_error status, title
|
40
|
+
@status = status
|
41
|
+
@json = {
|
42
|
+
errors: [
|
43
|
+
{title: title}
|
44
|
+
]
|
45
|
+
}
|
46
|
+
@content_type = JSONAPI[:content_type]
|
47
|
+
|
48
|
+
render(
|
49
|
+
json: @json,
|
50
|
+
status: @status,
|
51
|
+
content_type: @content_type
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "jsonapi_for_rails/controller/utils/model"
|
2
|
+
require "jsonapi_for_rails/controller/utils/render"
|
3
|
+
require "jsonapi_for_rails/controller/before_actions/sparse_fieldsets"
|
4
|
+
require "jsonapi_for_rails/controller/before_actions/include"
|
5
|
+
require "jsonapi_for_rails/controller/before_actions/record"
|
6
|
+
require "jsonapi_for_rails/controller/before_actions/relationship"
|
7
|
+
require "jsonapi_for_rails/controller/actions/object"
|
8
|
+
require "jsonapi_for_rails/controller/actions/relationship"
|
9
|
+
|
10
|
+
|
11
|
+
module JsonapiForRails::Controller
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
included do
|
15
|
+
#$stderr.puts "JsonapiForRails::Controller included into #{self}"
|
16
|
+
end
|
17
|
+
|
18
|
+
class_methods do
|
19
|
+
def acts_as_jsonapi_resources model: nil
|
20
|
+
#$stderr.puts "JsonapiForRails::Controller macro called from #{self}:\n acts_as_jsonapi_resources(model: #{model or 'nil'})"
|
21
|
+
|
22
|
+
include JsonapiForRails::Controller::Utils::Model
|
23
|
+
include JsonapiForRails::Controller::Utils::Render
|
24
|
+
include JsonapiForRails::Controller::BeforeActions::SparseFieldsets
|
25
|
+
include JsonapiForRails::Controller::BeforeActions::Include
|
26
|
+
include JsonapiForRails::Controller::BeforeActions::Record
|
27
|
+
include JsonapiForRails::Controller::BeforeActions::Relationship
|
28
|
+
include JsonapiForRails::Controller::Actions::Object
|
29
|
+
include JsonapiForRails::Controller::Actions::Relationship
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module JsonapiForRails::Model
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
#$stderr.puts "JsonapiForRails::Model included into #{self}"
|
6
|
+
|
7
|
+
# Define instance methods
|
8
|
+
class_exec do
|
9
|
+
def to_jsonapi_hash sparse_fieldset=nil
|
10
|
+
#$stderr.puts "JsonapiForRails::Controller::Actions::Object#show called"
|
11
|
+
|
12
|
+
# attributes
|
13
|
+
attrs = attributes.reject do |key, value|
|
14
|
+
key =~ /^id$|_id$/
|
15
|
+
end
|
16
|
+
if sparse_fieldset
|
17
|
+
attrs.reject! do |key, value|
|
18
|
+
not sparse_fieldset.find{|f| key.to_sym == f}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# relationships
|
23
|
+
relationships = {}
|
24
|
+
self.class.reflect_on_all_associations.each do |association|
|
25
|
+
if sparse_fieldset
|
26
|
+
next unless sparse_fieldset.find{|f| association.name == f}
|
27
|
+
end
|
28
|
+
relationship = {}
|
29
|
+
relationships[association.name] = relationship
|
30
|
+
|
31
|
+
# to-many relationship
|
32
|
+
if [
|
33
|
+
ActiveRecord::Reflection::HasManyReflection,
|
34
|
+
ActiveRecord::Reflection::HasAndBelongsToManyReflection
|
35
|
+
].include? association.class
|
36
|
+
|
37
|
+
relationship[:data] = []
|
38
|
+
#$stderr.puts "\nreading relationship '#{association.name}' of class '#{association.class}'"
|
39
|
+
#$stderr.puts "#{@record.send(association.name).inspect}"
|
40
|
+
self.send(association.name).each do |record|
|
41
|
+
#$stderr.puts "self.#{association.name}: #{record.class}"
|
42
|
+
relationship[:data] << {
|
43
|
+
type: record.class.to_s.underscore.pluralize, # TODO: factor out type generation from class
|
44
|
+
id: record.id
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
# to-one relationship
|
49
|
+
elsif [
|
50
|
+
|
51
|
+
ActiveRecord::Reflection::HasOneReflection,
|
52
|
+
ActiveRecord::Reflection::BelongsToReflection
|
53
|
+
].include? association.class
|
54
|
+
|
55
|
+
relationship[:data] = nil
|
56
|
+
#$stderr.puts "\nreading relationship '#{association.name}' of class '#{association.class}'"
|
57
|
+
#$stderr.puts "#{self.send(association.name).inspect}"
|
58
|
+
if record = self.send(association.name)
|
59
|
+
relationship[:data] = {
|
60
|
+
type: record.class.to_s.underscore.pluralize, # TODO: factor out type generation from class
|
61
|
+
id: record.id
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# message
|
68
|
+
{
|
69
|
+
=begin
|
70
|
+
meta: {
|
71
|
+
generated_by_class: "#{self.class}"
|
72
|
+
},
|
73
|
+
=end
|
74
|
+
data: {
|
75
|
+
type: jsonapi_model_type,
|
76
|
+
id: self.id,
|
77
|
+
|
78
|
+
attributes: attrs,
|
79
|
+
|
80
|
+
relationships: relationships
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
def jsonapi_model_type
|
87
|
+
"#{self.class}".underscore.pluralize.to_sym
|
88
|
+
end
|
89
|
+
|
90
|
+
private :jsonapi_model_type
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
|
2
|
+
# TODO: send pull request to add jsonapi_for_rails to JSON API implementations list (http://jsonapi.org/implementations/)
|
3
|
+
# TODO: double-check the installation instructions in README.md
|
4
|
+
# TODO: 'Contributing' section in README.md
|
5
|
+
|
6
|
+
require "jsonapi_for_rails/version"
|
7
|
+
require "jsonapi_for_rails/controller"
|
8
|
+
require "jsonapi_for_rails/model"
|
9
|
+
|
10
|
+
# Add 'acts_as_jsonapi_resources' class method to controllers
|
11
|
+
ActionController::Metal.send :include, JsonapiForRails::Controller
|
12
|
+
|
13
|
+
# Add 'to_jsonapi_hash' instance method to models
|
14
|
+
ActiveRecord::Base.send :include, JsonapiForRails::Model
|
15
|
+
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jsonapi_for_rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Doga Armangil
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-02-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.0.0.beta2
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '5.1'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 5.0.0.beta2
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '5.1'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: sqlite3
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
description: Jsonapi for Rails empowers your JSON API (http://jsonapi.org/format/)
|
48
|
+
compliant Rails APIs. Implement your REST API with very little coding.
|
49
|
+
email:
|
50
|
+
- doga.armangil@alumni.epfl.ch
|
51
|
+
executables: []
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files: []
|
54
|
+
files:
|
55
|
+
- MIT-LICENSE
|
56
|
+
- README.md
|
57
|
+
- Rakefile
|
58
|
+
- lib/jsonapi_for_rails.rb
|
59
|
+
- lib/jsonapi_for_rails/controller.rb
|
60
|
+
- lib/jsonapi_for_rails/controller/actions/object.rb
|
61
|
+
- lib/jsonapi_for_rails/controller/actions/relationship.rb
|
62
|
+
- lib/jsonapi_for_rails/controller/before_actions/include.rb
|
63
|
+
- lib/jsonapi_for_rails/controller/before_actions/record.rb
|
64
|
+
- lib/jsonapi_for_rails/controller/before_actions/relationship.rb
|
65
|
+
- lib/jsonapi_for_rails/controller/before_actions/sparse_fieldsets.rb
|
66
|
+
- lib/jsonapi_for_rails/controller/utils/model.rb
|
67
|
+
- lib/jsonapi_for_rails/controller/utils/render.rb
|
68
|
+
- lib/jsonapi_for_rails/model.rb
|
69
|
+
- lib/jsonapi_for_rails/version.rb
|
70
|
+
- lib/tasks/jsonapi_for_rails_tasks.rake
|
71
|
+
homepage: https://github.com/doga/jsonapi_for_rails
|
72
|
+
licenses:
|
73
|
+
- MIT
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.5.1
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: A Rails plugin for providing JSON API compliant APIs with very little coding.
|
95
|
+
test_files: []
|