roar-jsonapi 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +37 -0
- data/.travis.yml +14 -0
- data/.yardopts +5 -0
- data/CONTRIBUTING.md +31 -0
- data/Gemfile +11 -0
- data/ISSUE_TEMPLATE.md +20 -0
- data/LICENSE +20 -0
- data/README.markdown +127 -0
- data/Rakefile +12 -0
- data/lib/roar/json/json_api.rb +156 -0
- data/lib/roar/json/json_api/declarative.rb +196 -0
- data/lib/roar/json/json_api/defaults.rb +25 -0
- data/lib/roar/json/json_api/document.rb +104 -0
- data/lib/roar/json/json_api/for_collection.rb +35 -0
- data/lib/roar/json/json_api/member_name.rb +57 -0
- data/lib/roar/json/json_api/meta.rb +56 -0
- data/lib/roar/json/json_api/options.rb +98 -0
- data/lib/roar/json/json_api/version.rb +7 -0
- data/roar-jsonapi.gemspec +25 -0
- data/test/jsonapi/collection_render_test.rb +399 -0
- data/test/jsonapi/fieldsets_options_test.rb +161 -0
- data/test/jsonapi/fieldsets_test.rb +293 -0
- data/test/jsonapi/member_name_test.rb +91 -0
- data/test/jsonapi/post_test.rb +78 -0
- data/test/jsonapi/render_test.rb +281 -0
- data/test/jsonapi/representer.rb +112 -0
- data/test/jsonapi/resource_linkage_test.rb +88 -0
- data/test/test_helper.rb +42 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ece64905f608afe23b1ff500b22cc2735773ff4d
|
4
|
+
data.tar.gz: 8b9b74e066c310d7dfcc1fb615ec050df9e825ea
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f9617c6cbe337a9d0533f67426fd6b8ce83420e7873f783acea631e7227a39e59097dd11b4a80916d694b2d5771dd9a47da2e7212c12096960eaf72e7058ce3b
|
7
|
+
data.tar.gz: 320c347be830d71313d237cb66460dfa1ef63ae0492c73d6064f3e5cba0bf50497a1c51769c249ae9a7047681063d659d81e1b0f97aa2fe9e0f2ed797fd86d20
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 1.9
|
3
|
+
|
4
|
+
Style/AlignHash:
|
5
|
+
EnforcedHashRocketStyle: table
|
6
|
+
EnforcedColonStyle: table
|
7
|
+
|
8
|
+
Style/AndOr:
|
9
|
+
EnforcedStyle: conditionals
|
10
|
+
|
11
|
+
Style/BlockDelimiters:
|
12
|
+
Enabled: true
|
13
|
+
EnforcedStyle: semantic
|
14
|
+
IgnoredMethods:
|
15
|
+
- lambda
|
16
|
+
- proc
|
17
|
+
- it
|
18
|
+
- link
|
19
|
+
|
20
|
+
Style/Lambda:
|
21
|
+
Enabled: false
|
22
|
+
EnforcedStyle: literal
|
23
|
+
|
24
|
+
Style/LambdaCall:
|
25
|
+
EnforcedStyle: braces
|
26
|
+
|
27
|
+
Metrics/LineLength:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Metrics/MethodLength:
|
31
|
+
Max: 15
|
32
|
+
|
33
|
+
Style/PredicateName:
|
34
|
+
NameWhitelist:
|
35
|
+
- is_a?
|
36
|
+
- has_one
|
37
|
+
- has_many
|
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
## How to contribute to Roar
|
2
|
+
|
3
|
+
#### **Did you find a bug?**
|
4
|
+
|
5
|
+
* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/trailblazer/roar-jsonapi/issues).
|
6
|
+
|
7
|
+
* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/trailblazer/roar-jsonapi/issues/new). Be sure to follow the issue template.
|
8
|
+
|
9
|
+
#### **Did you write a patch that fixes a bug?**
|
10
|
+
|
11
|
+
* Open a new GitHub pull request with the patch.
|
12
|
+
|
13
|
+
* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
|
14
|
+
|
15
|
+
* All code in pull requests is assumed to be MIT licensed. Do not submit a pull request if that isn't the case.
|
16
|
+
|
17
|
+
#### **Do you intend to add a new feature or change an existing one?**
|
18
|
+
|
19
|
+
* Suggest your change in the [Trailblazer Gitter Room](https://gitter.im/trailblazer/chat) and start writing code.
|
20
|
+
|
21
|
+
* Do not open an issue on GitHub until you have collected positive feedback about the change. GitHub issues are primarily intended for bug reports and fixes.
|
22
|
+
|
23
|
+
#### **Do you have questions using Roar?**
|
24
|
+
|
25
|
+
* Ask any questions about how to use Roar in the [Trailblazer Gitter Room](https://gitter.im/trailblazer/chat). Github issues are restricted to bug reports and fixes.
|
26
|
+
|
27
|
+
* GitHub Issues should not be used as a help forum and any such issues will be closed.
|
28
|
+
|
29
|
+
#### **Do you want to contribute to the Roar documentation?**
|
30
|
+
|
31
|
+
* Roar documentation is provided via the [Trailblazer site](http://trailblazer.to/gems/roar/) and not the repository readme. Please add your contributions to the [Trailblazer site repository](https://github.com/trailblazer/trailblazer.github.io)
|
data/Gemfile
ADDED
data/ISSUE_TEMPLATE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Note: If you have a question about Roar, would like help using
|
2
|
+
Roar, want to request a feature, or do anything else other than
|
3
|
+
submit a bug report, please use the Trailblazer gitter channel.
|
4
|
+
|
5
|
+
### Complete Description of Issue
|
6
|
+
|
7
|
+
|
8
|
+
### Steps to reproduce
|
9
|
+
|
10
|
+
|
11
|
+
### Expected behavior
|
12
|
+
Tell us what should happen
|
13
|
+
|
14
|
+
### Actual behavior
|
15
|
+
Tell us what happens instead
|
16
|
+
|
17
|
+
### System configuration
|
18
|
+
**Roar version**:
|
19
|
+
|
20
|
+
### Full Backtrace of Exception (if any)
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 - 2017 Nick Sutterer and the roar contributors
|
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.markdown
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# Roar JSON API
|
2
|
+
|
3
|
+
_Resource-Oriented Architectures in Ruby._
|
4
|
+
|
5
|
+
[](https://gitter.im/trailblazer/chat)
|
6
|
+
[](http://trailblazer.to/newsletter/)
|
7
|
+
[](https://travis-ci.org/trailblazer/roar-jsonapi)
|
8
|
+
[](http://badge.fury.io/rb/roar-jsonapi)
|
9
|
+
|
10
|
+
Roar JSON API provides support for [JSON API](http://jsonapi.org/), a specification for building APIs in JSON. It can render _and_ parse singular and collection documents.
|
11
|
+
|
12
|
+
### Resource
|
13
|
+
|
14
|
+
A minimal representation of a Resource can be defined as follows:
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
require 'roar/json/json_api'
|
18
|
+
|
19
|
+
class SongsRepresenter < Roar::Decorator
|
20
|
+
include Roar::JSON::JSONAPI.resource :songs
|
21
|
+
|
22
|
+
attributes do
|
23
|
+
property :title
|
24
|
+
end
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
Properties (or attributes) of the represented model are defined within an
|
29
|
+
`attributes` block.
|
30
|
+
|
31
|
+
An `id` property will automatically defined when using Roar JSON API.
|
32
|
+
|
33
|
+
### Relationships
|
34
|
+
|
35
|
+
To define relationships, use `::has_one` or `::has_many` with either an inline
|
36
|
+
or a standalone representer (specified with the `extend:` or `decorates:` option).
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
class SongsRepresenter < Roar::Decorator
|
40
|
+
include Roar::JSON::JSONAPI.resource :songs
|
41
|
+
|
42
|
+
has_one :album do
|
43
|
+
property :title
|
44
|
+
end
|
45
|
+
|
46
|
+
has_many :musicians, extend: MusicianRepresenter
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
### Meta information
|
51
|
+
|
52
|
+
Meta information can be included into rendered singular and collection documents in two ways.
|
53
|
+
|
54
|
+
You can define meta information on your collection object and then let Roar compile it.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
class SongsRepresenter < Roar::Decorator
|
58
|
+
include Roar::JSON::JSONAPI.resource :songs
|
59
|
+
|
60
|
+
meta toplevel: true do
|
61
|
+
property :page
|
62
|
+
property :total
|
63
|
+
end
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
Your collection object must expose the respective methods.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
collection.page #=> 1
|
71
|
+
collection.total #=> 12
|
72
|
+
```
|
73
|
+
|
74
|
+
This will render the `{"meta": {"page": 1, "total": 12}}` hash into the JSON API document.
|
75
|
+
|
76
|
+
Alternatively, you can provide meta information as a hash when rendering. Any values also defined on your object will be overriden.
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
collection.to_json(meta: {page: params["page"], total: collection.size})
|
80
|
+
```
|
81
|
+
|
82
|
+
Both methods work for singular documents too.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
class SongsRepresenter < Roar::Decorator
|
86
|
+
include Roar::JSON::JSONAPI.resource :songs
|
87
|
+
|
88
|
+
meta do
|
89
|
+
property :label
|
90
|
+
property :format
|
91
|
+
end
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
song.to_json(meta: { label: 'EMI' })
|
97
|
+
```
|
98
|
+
|
99
|
+
If you need more functionality (and parsing), please let us know.
|
100
|
+
|
101
|
+
### Usage
|
102
|
+
|
103
|
+
As JSON API per definition can represent singular models and collections you have two entry points.
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
SongsRepresenter.prepare(Song.find(1)).to_json
|
107
|
+
SongsRepresenter.prepare(Song.new).from_json("..")
|
108
|
+
```
|
109
|
+
|
110
|
+
Singular models can use the representer module directly.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
SongsRepresenter.for_collection.prepare([Song.find(1), Song.find(2)]).to_json
|
114
|
+
SongsRepresenter.for_collection.prepare([Song.new, Song.new]).from_json("..")
|
115
|
+
```
|
116
|
+
|
117
|
+
|
118
|
+
Parsing currently works great with singular documents - for collections, we are still working out how to encode the application semantics. Feel free to help.
|
119
|
+
|
120
|
+
## Support
|
121
|
+
|
122
|
+
Questions? Need help? Free 1st Level Support on irc.freenode.org#roar !
|
123
|
+
We also have a [mailing list](https://groups.google.com/forum/?fromgroups#!forum/roar-talk), yiha!
|
124
|
+
|
125
|
+
## License
|
126
|
+
|
127
|
+
Roar is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'roar/json'
|
2
|
+
require 'roar/decorator'
|
3
|
+
require 'set'
|
4
|
+
|
5
|
+
require 'roar/json/json_api/member_name'
|
6
|
+
|
7
|
+
require 'roar/json/json_api/defaults'
|
8
|
+
require 'roar/json/json_api/meta'
|
9
|
+
require 'roar/json/json_api/declarative'
|
10
|
+
require 'roar/json/json_api/for_collection'
|
11
|
+
require 'roar/json/json_api/options'
|
12
|
+
require 'roar/json/json_api/document'
|
13
|
+
|
14
|
+
module Roar
|
15
|
+
module JSON
|
16
|
+
module JSONAPI
|
17
|
+
# Include to define a JSON API Resource and make API methods available to
|
18
|
+
# your `Roar::Decorator`.
|
19
|
+
#
|
20
|
+
# @api public
|
21
|
+
class Resource < Module
|
22
|
+
# @param [Symbol, String] type type name of this resource.
|
23
|
+
# @option options [Symbol] :id_key custom ID key for this resource.
|
24
|
+
def initialize(type, options = {})
|
25
|
+
@type = type
|
26
|
+
@id_key = options.fetch(:id_key, :id)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Hook called when module is included
|
32
|
+
#
|
33
|
+
# @param [Class,Module] base
|
34
|
+
# the module or class including JSONAPI
|
35
|
+
#
|
36
|
+
# @return [undefined]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
# @see http://www.ruby-doc.org/core/Module.html#method-i-included
|
40
|
+
def included(base)
|
41
|
+
base.send(:include, JSONAPI::Mixin)
|
42
|
+
base.type(@type)
|
43
|
+
base.property(@id_key, as: :id, render_filter: ->(input, _opts) {
|
44
|
+
input.to_s
|
45
|
+
})
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Include to define a JSON API Resource and make API methods available to
|
50
|
+
# your `Roar::Decorator`.
|
51
|
+
#
|
52
|
+
# @example Basic Usage
|
53
|
+
# class SongsRepresenter < Roar::Decorator
|
54
|
+
# include Roar::JSON::JSONAPI.resource :songs
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# @example Custom ID key
|
58
|
+
# class SongsRepresenter < Roar::Decorator
|
59
|
+
# include Roar::JSON::JSONAPI.resource :songs, id_key: :song_id
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# @param (see Resource.initialize)
|
63
|
+
# @option options (see Resource.initialize)
|
64
|
+
#
|
65
|
+
# @see Mixin
|
66
|
+
# @api public
|
67
|
+
def self.resource(type, options = {})
|
68
|
+
Resource.new(type, options)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Include to make API methods available to your `Roar::Decorator`.
|
72
|
+
#
|
73
|
+
# Unlike {Resource}, you must define a `type` (by calling
|
74
|
+
# {Declarative#type}) and `id` property separately.
|
75
|
+
#
|
76
|
+
# @example Basic Usage
|
77
|
+
# class SongsRepresenter < Roar::Decorator
|
78
|
+
# include Roar::JSON::JSONAPI::Mixin
|
79
|
+
#
|
80
|
+
# type :songs
|
81
|
+
# property :id
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# @see Resource
|
85
|
+
# @api semi-public
|
86
|
+
module Mixin
|
87
|
+
# Hook called when module is included
|
88
|
+
#
|
89
|
+
# @param [Class,Module] base
|
90
|
+
# the module or class including JSONAPI
|
91
|
+
#
|
92
|
+
# @return [undefined]
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
# @see http://www.ruby-doc.org/core/Module.html#method-i-included
|
96
|
+
def self.included(base)
|
97
|
+
base.class_eval do
|
98
|
+
feature Roar::JSON
|
99
|
+
feature Roar::Hypermedia
|
100
|
+
feature JSONAPI::Defaults, JSONAPI::Meta
|
101
|
+
extend JSONAPI::Declarative
|
102
|
+
extend JSONAPI::ForCollection
|
103
|
+
include JSONAPI::Document
|
104
|
+
self.representation_wrap = :data
|
105
|
+
|
106
|
+
nested :relationships do
|
107
|
+
end
|
108
|
+
|
109
|
+
nested :included do
|
110
|
+
def to_hash(*)
|
111
|
+
super.flat_map { |_, resource| resource }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# @api private
|
119
|
+
module Renderer
|
120
|
+
class Links
|
121
|
+
def call(res, _options)
|
122
|
+
tuples = (res.delete('links') || []).collect { |link|
|
123
|
+
[JSONAPI::MemberName.(link['rel']), link['href']]
|
124
|
+
}
|
125
|
+
|
126
|
+
::Hash[tuples] # NOTE: change to tuples.to_h when dropping < 2.1.
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# @api private
|
132
|
+
module Fragment
|
133
|
+
Included = ->(included, options) do
|
134
|
+
return unless included && included.any?
|
135
|
+
return if options[:included] == false
|
136
|
+
|
137
|
+
type_and_id_seen = Set.new
|
138
|
+
|
139
|
+
included = included.select { |object|
|
140
|
+
type_and_id_seen.add? [object['type'], object['id']]
|
141
|
+
}
|
142
|
+
|
143
|
+
included
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# @api private
|
149
|
+
module HashUtils
|
150
|
+
def store_if_any(hash, key, value)
|
151
|
+
hash[key] = value if value && value.any?
|
152
|
+
end
|
153
|
+
module_function :store_if_any
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|