jsonapi_for_rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|