shamu 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -1
- data/README.md +1 -2
- data/lib/shamu/features/features_service.rb +1 -1
- data/lib/shamu/json_api/README.md +1 -0
- data/lib/shamu/json_api/base_builder.rb +67 -0
- data/lib/shamu/json_api/context.rb +69 -0
- data/lib/shamu/json_api/entity_serializer.rb +10 -0
- data/lib/shamu/json_api/error.rb +29 -0
- data/lib/shamu/json_api/error_builder.rb +78 -0
- data/lib/shamu/json_api/relationship_builder.rb +16 -0
- data/lib/shamu/json_api/resource_builder.rb +81 -0
- data/lib/shamu/json_api/response.rb +99 -0
- data/lib/shamu/json_api/serializer.rb +33 -0
- data/lib/shamu/json_api/support.rb +8 -0
- data/lib/shamu/json_api.rb +13 -0
- data/lib/shamu/locale/en.yml +6 -1
- data/lib/shamu/version.rb +1 -1
- data/lib/shamu.rb +1 -0
- data/spec/lib/shamu/json_api/base_builder_spec.rb +42 -0
- data/spec/lib/shamu/json_api/common_builder_spec.rb +0 -0
- data/spec/lib/shamu/json_api/context_spec.rb +31 -0
- data/spec/lib/shamu/json_api/error_builder_spec.rb +31 -0
- data/spec/lib/shamu/json_api/relationship_builder_spec.rb +5 -0
- data/spec/lib/shamu/json_api/resource_builder_spec.rb +52 -0
- data/spec/lib/shamu/json_api/response_spec.rb +42 -0
- metadata +28 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 027f0ea99b484c02d9986999b39f3025dee4b4a3
|
4
|
+
data.tar.gz: 0eb00f3878a9718c979e511e8930cf81d8d708e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75946cc4efced52d7a3cf58bb673c75cdd2d9a7aee479b91f3fff4c96d485b74319f07c22345c616075a598948ee861eecd816046aefd37f9761321981c3d11e
|
7
|
+
data.tar.gz: 63784d0e8b20db671ba1563c19146ecea4e2fa3728098fc1a79db42e8ef2ebf57a7e68716c9aa349676dff841b6c92f4c73fc81f660df3bbe3fca4d1a8b953ba
|
data/.rubocop.yml
CHANGED
@@ -79,7 +79,10 @@ Style/MultilineBlockChain:
|
|
79
79
|
Enabled: false
|
80
80
|
|
81
81
|
Style/OptionHash:
|
82
|
-
|
82
|
+
Enabled: true
|
83
|
+
|
84
|
+
Style/ParallelAssignment:
|
85
|
+
Enabled: false
|
83
86
|
|
84
87
|
Style/RegexpLiteral:
|
85
88
|
Enabled: false
|
@@ -111,6 +114,9 @@ Style/TrailingBlankLines:
|
|
111
114
|
Style/TrailingCommaInLiteral:
|
112
115
|
Enabled: false
|
113
116
|
|
117
|
+
Style/TrivialAccessors:
|
118
|
+
Enabled: false
|
119
|
+
|
114
120
|
Style/UnneededInterpolation:
|
115
121
|
Enabled: false
|
116
122
|
|
data/README.md
CHANGED
@@ -158,7 +158,7 @@ module Shamu
|
|
158
158
|
|
159
159
|
# @param [String] path of the default config file.
|
160
160
|
# @return [String]
|
161
|
-
def default_config_path=( path )
|
161
|
+
def default_config_path=( path )
|
162
162
|
@default_config_path = path
|
163
163
|
end
|
164
164
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
http://jsonapi.org
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Shamu
|
2
|
+
module JsonApi
|
3
|
+
|
4
|
+
# Used by a {Serilaizer} to write fields and relationships
|
5
|
+
class BaseBuilder
|
6
|
+
|
7
|
+
# @param [Context] context the current serialization context.
|
8
|
+
def initialize( context )
|
9
|
+
@context = context
|
10
|
+
@output = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# @overload identifier( type, id )
|
14
|
+
# @param [String] type of the resource.
|
15
|
+
# @param [Object] id of the resource.
|
16
|
+
# @overload identifier( resource )
|
17
|
+
# @param [#json_api_type,#id] resource an object that responds to `json_api_type` and `id`
|
18
|
+
#
|
19
|
+
# Write a resource linkage info.
|
20
|
+
#
|
21
|
+
# @return [void]
|
22
|
+
def identifier( type, id = nil )
|
23
|
+
type, id = type.json_api_type, type.id if type.respond_to? :json_api_type
|
24
|
+
|
25
|
+
output[:type] = type.to_s
|
26
|
+
output[:id] = id.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
# Write a link to another resource.
|
30
|
+
#
|
31
|
+
# @param [String,Symbol] name of the link.
|
32
|
+
# @param [String] url
|
33
|
+
# @param [Hash] meta optional additional meta information.
|
34
|
+
# @return [void]
|
35
|
+
def link( name, url, meta: nil )
|
36
|
+
links = ( output[:links] ||= {} )
|
37
|
+
|
38
|
+
if meta # rubocop:disable Style/ConditionalAssignment
|
39
|
+
links[ name.to_sym ] = { href: url, meta: meta }
|
40
|
+
else
|
41
|
+
links[ name.to_sym ] = url
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Add a meta field.
|
46
|
+
# @param [String,Symbol] name of the meta field.
|
47
|
+
# @param [Object] vlaue that can be converted to a JSON primitive type.
|
48
|
+
# @return [void]
|
49
|
+
def meta( name, value )
|
50
|
+
meta = ( output[:meta] ||= {} )
|
51
|
+
meta[ name.to_sym ] = value
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [Hash] the results output as JSON safe hash.
|
55
|
+
def compile
|
56
|
+
fail JsonApi::IncompleteResourceError unless output[:type]
|
57
|
+
output
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
attr_reader :context
|
63
|
+
attr_reader :output
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Shamu
|
2
|
+
module JsonApi
|
3
|
+
class Context
|
4
|
+
|
5
|
+
def initialize( fields: nil )
|
6
|
+
@included_resources = {}
|
7
|
+
@all_resources = Set.new
|
8
|
+
@fields = parse_fields( fields )
|
9
|
+
end
|
10
|
+
|
11
|
+
# Add an included resource for a compound response.
|
12
|
+
#
|
13
|
+
# @param [Object] resource to be serialized.
|
14
|
+
# @param [Serializer] the serializer to use to serialize the object. If
|
15
|
+
# not provided a default {Serializer} will be chosen.
|
16
|
+
# @return [resource]
|
17
|
+
# @yield (builder)
|
18
|
+
# @yieldparam [ResourceBuilder] builder to write embedded resource to.
|
19
|
+
def include_resource( resource, serializer = nil, &block )
|
20
|
+
return if all_resources.include?( resource )
|
21
|
+
|
22
|
+
all_resources << resource
|
23
|
+
included_resources[resource] ||= { serializer: serializer, block: block }
|
24
|
+
end
|
25
|
+
|
26
|
+
# Collects all the currently included resources and resets the queue.
|
27
|
+
def collect_included_resources
|
28
|
+
included = included_resources.dup
|
29
|
+
@included_resources = {}
|
30
|
+
included
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Boolean] true if there are any pending included resources.
|
34
|
+
def included_resources?
|
35
|
+
included_resources.any?
|
36
|
+
end
|
37
|
+
|
38
|
+
# Check to see if the field should be included in the JSON API output.
|
39
|
+
#
|
40
|
+
# @param [Symbol] type the resource type in question.
|
41
|
+
# @param [Symbol] name of the field on the resouce in question.
|
42
|
+
# @return [Boolean] true if the
|
43
|
+
def include_field?( type, name )
|
44
|
+
return true unless type_fields = fields[ type ]
|
45
|
+
|
46
|
+
type_fields.include?( name )
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_reader :all_resources
|
52
|
+
attr_reader :included_resources
|
53
|
+
attr_reader :fields
|
54
|
+
|
55
|
+
def parse_fields( raw )
|
56
|
+
return {} unless raw
|
57
|
+
|
58
|
+
raw.each_with_object( {} ) do |(type, fields), parsed|
|
59
|
+
fields = fields.split( "," ) if fields.is_a?( String )
|
60
|
+
|
61
|
+
parsed[ type.to_sym ] = fields.map do |field|
|
62
|
+
field = field.strip if field.is_a? String
|
63
|
+
field.to_sym
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "i18n"
|
2
|
+
|
3
|
+
module Shamu
|
4
|
+
|
5
|
+
module JsonApi
|
6
|
+
# A generic error class for problems with shamu JSON API.
|
7
|
+
class Error < Shamu::Error
|
8
|
+
private
|
9
|
+
|
10
|
+
def translation_scope
|
11
|
+
super.dup.insert( 1, :json_api )
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
# Raised if an {ResourceBuilder#identifier} was not built.
|
17
|
+
class IncompleteResourceError < Error
|
18
|
+
def initialize( message = :incomplete_resource )
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class IdentifierRequiredError < Error
|
24
|
+
def initialize( message = :identifier_required )
|
25
|
+
super
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Shamu
|
2
|
+
module JsonApi
|
3
|
+
|
4
|
+
# Build an error response object.
|
5
|
+
class ErrorBuilder
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@output = { id: SecureRandom.uuid }
|
9
|
+
end
|
10
|
+
|
11
|
+
# @param [String] id unique id for this occurrence of the error.
|
12
|
+
def id( id )
|
13
|
+
output[:id] = id
|
14
|
+
end
|
15
|
+
|
16
|
+
# Summary of the error.
|
17
|
+
# @param [Integer] http_status code.
|
18
|
+
# @param [String,Symbol] code application specific code for the error.
|
19
|
+
# @param [String] human friendly title for the error.
|
20
|
+
def summary( http_status, code, title = nil )
|
21
|
+
output[:status] = http_status.to_s
|
22
|
+
output[:code] = code.to_s
|
23
|
+
output[:title] = title || code.to_s.titleize
|
24
|
+
end
|
25
|
+
|
26
|
+
# Summarize an exception as an error.
|
27
|
+
# @param [Exception] exception
|
28
|
+
# @param [Integer] http_status code. Default 400.
|
29
|
+
def exception( exception, http_status = nil )
|
30
|
+
http_status ||= 400
|
31
|
+
|
32
|
+
name = exception.class.name.demodulize.gsub( /Error$/, "" )
|
33
|
+
summary http_status, name.underscore, name.titleize
|
34
|
+
detail exception.message
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [String] message details about the error.
|
38
|
+
def detail( message )
|
39
|
+
output[:detail] = message
|
40
|
+
end
|
41
|
+
|
42
|
+
# Write a link to error information.
|
43
|
+
#
|
44
|
+
# @param [String,Symbol] name of the link.
|
45
|
+
# @param [String] url
|
46
|
+
# @param [Hash] meta optional additional meta information.
|
47
|
+
# @return [void]
|
48
|
+
def link( name, url, meta: nil )
|
49
|
+
links = ( output[:links] ||= {} )
|
50
|
+
|
51
|
+
if meta # rubocop:disable Style/ConditionalAssignment
|
52
|
+
links[ name.to_sym ] = { href: url, meta: meta }
|
53
|
+
else
|
54
|
+
links[ name.to_sym ] = url
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Add a meta field.
|
59
|
+
# @param [String,Symbol] name of the meta field.
|
60
|
+
# @param [Object] value that can be converted to a JSON primitive type.
|
61
|
+
# @return [void]
|
62
|
+
def meta( name, value )
|
63
|
+
meta = ( output[:meta] ||= {} )
|
64
|
+
meta[ name.to_sym ] = value
|
65
|
+
end
|
66
|
+
|
67
|
+
# @return [Hash] the results output as JSON safe hash.
|
68
|
+
def compile
|
69
|
+
output
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
attr_reader :output
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "shamu/json_api/base_builder"
|
2
|
+
|
3
|
+
module Shamu
|
4
|
+
module JsonApi
|
5
|
+
|
6
|
+
# Build a relationship from one resource to another.
|
7
|
+
class RelationshipBuilder < BaseBuilder
|
8
|
+
|
9
|
+
# (see Context#include_resource)
|
10
|
+
def include_resource( resource, serializer = nil, &block )
|
11
|
+
context.include_resource resource, serializer, &block
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "shamu/json_api/base_builder"
|
2
|
+
|
3
|
+
module Shamu
|
4
|
+
module JsonApi
|
5
|
+
|
6
|
+
# Used by a {Serilaizer} to write fields and relationships
|
7
|
+
class ResourceBuilder < BaseBuilder
|
8
|
+
|
9
|
+
# @overload attribute( attributes )
|
10
|
+
# @param [Hash] attributes to write.
|
11
|
+
# @overload attribute( name, value )
|
12
|
+
# @param [String, Symbol] name of the attribute.
|
13
|
+
# @param [Object] value that can be persited to a JSON primitive value.
|
14
|
+
#
|
15
|
+
# Write one or more attributes to the output.
|
16
|
+
#
|
17
|
+
# @return [void]
|
18
|
+
def attribute( name_or_hash, value = nil )
|
19
|
+
require_identifier!
|
20
|
+
|
21
|
+
if value
|
22
|
+
add_attribute name_or_hash, value
|
23
|
+
else
|
24
|
+
name_or_hash.each do |n, v|
|
25
|
+
add_attribute n, v
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Build a relationship reference.
|
31
|
+
#
|
32
|
+
# ```
|
33
|
+
# relationship :author do |builder|
|
34
|
+
# builder.identifier author
|
35
|
+
# builder.link :related, author_url author
|
36
|
+
# builder.link :self, book_author_url( book, author )
|
37
|
+
# end
|
38
|
+
# ```
|
39
|
+
#
|
40
|
+
# @param [String,Symbol] name of the relationship.
|
41
|
+
# @return [void]
|
42
|
+
# @yield (builder)
|
43
|
+
# @yieldparam [RelationshipBuilder] builder used to define the properties
|
44
|
+
# of the relationship.
|
45
|
+
def relationship( name, &block )
|
46
|
+
require_identifier!
|
47
|
+
|
48
|
+
return unless context.include_field?( type, name )
|
49
|
+
|
50
|
+
builder = RelationshipBuilder.new( context )
|
51
|
+
yield builder
|
52
|
+
|
53
|
+
relationships = ( output[:relationships] ||= {} )
|
54
|
+
relationships[ name.to_sym ] = builder.compile
|
55
|
+
end
|
56
|
+
|
57
|
+
# (see BaseBuilder#identifier)
|
58
|
+
def identifier( * )
|
59
|
+
super.tap do
|
60
|
+
@type = output[:type]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
attr_reader :type
|
67
|
+
|
68
|
+
def require_identifier!
|
69
|
+
fail IdentifierRequiredError unless @type
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_attribute( name, value )
|
73
|
+
return unless context.include_field?( type, name )
|
74
|
+
|
75
|
+
attributes = ( output[:attributes] ||= {} )
|
76
|
+
attributes[ name.to_sym ] = value
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Shamu
|
2
|
+
module JsonApi
|
3
|
+
|
4
|
+
# Build a JSON API response from one or more resources.
|
5
|
+
class Response < BaseBuilder
|
6
|
+
|
7
|
+
# Output a single resource as the response data.
|
8
|
+
# @param [Object] resource to write.
|
9
|
+
# @param [Serializer] serializer used to write the resource state.
|
10
|
+
# @yield (builder)
|
11
|
+
# @yieldparam [ResourceBuilder] builder used write the resources fields
|
12
|
+
# and meta.
|
13
|
+
#
|
14
|
+
# @return [void]
|
15
|
+
def resource( resource, serializer = nil, &block )
|
16
|
+
output[:data] = build_resource( resource, serializer, &block )
|
17
|
+
end
|
18
|
+
|
19
|
+
# Output a single resource as the response data.
|
20
|
+
#
|
21
|
+
# @param [Enumerable<Object>] resources to write.
|
22
|
+
# @param [Serializer] serializer used to write the resource state.
|
23
|
+
# @yield (builder, resource)
|
24
|
+
# @yieldparam [ResourceBuilder] builder used write the resources fields
|
25
|
+
# and meta.
|
26
|
+
# @yieldparam [Object] resource being written.
|
27
|
+
# @return [void]
|
28
|
+
def resources( collection, serializer = nil, &block )
|
29
|
+
output[:data] =
|
30
|
+
collection.map do |resource|
|
31
|
+
build_resource resource, serializer, &block
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# @overload error( exception, http_status = nil )
|
36
|
+
# @param (see ErrorBuilder#exception)
|
37
|
+
# @overload error( &block )
|
38
|
+
# @yield (builder)
|
39
|
+
# @yieldparam [ErrorBuilder] builder used to describe the error.
|
40
|
+
#
|
41
|
+
# @return [void]
|
42
|
+
def error( exception = nil, http_status = nil, &block )
|
43
|
+
builder = ErrorBuilder.new
|
44
|
+
|
45
|
+
if block_given?
|
46
|
+
yield builder
|
47
|
+
else
|
48
|
+
builder.exception( exception, http_status )
|
49
|
+
end
|
50
|
+
|
51
|
+
errors = ( output[:errors] ||= [] )
|
52
|
+
errors << builder.compile
|
53
|
+
end
|
54
|
+
|
55
|
+
# (see BaseBuilder#compile)
|
56
|
+
def compile
|
57
|
+
@compiled ||= begin
|
58
|
+
compiled = output.dup
|
59
|
+
compiled[:jsonapi] = { version: "1.0" }
|
60
|
+
|
61
|
+
while context.included_resources?
|
62
|
+
included = ( compiled[ :included ] ||= [] )
|
63
|
+
context.collect_included_resources.each do |resource, options|
|
64
|
+
included << build_resource( resource, options[:serializer], &options[:block] )
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
compiled
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_json
|
73
|
+
compile.to_json
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
compile.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
private :identifier
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def build_resource( resource, serializer, &block )
|
85
|
+
fail "A block is required if no serializer is given" if !serializer && !block_given?
|
86
|
+
|
87
|
+
builder = ResourceBuilder.new( context )
|
88
|
+
if serializer
|
89
|
+
serializer.serialize( builder )
|
90
|
+
else
|
91
|
+
yield builder
|
92
|
+
end
|
93
|
+
|
94
|
+
builder.compile
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Shamu
|
2
|
+
module JsonApi
|
3
|
+
|
4
|
+
# Serialize an object to a JSON API stream.
|
5
|
+
class Serializer
|
6
|
+
|
7
|
+
# @param [Object] resource to be serialized.
|
8
|
+
def initialize( resource )
|
9
|
+
@resource = resource
|
10
|
+
end
|
11
|
+
|
12
|
+
# Serialize the {#resource} to the builder.
|
13
|
+
# @param [ResourceBuilder] builder to write to.
|
14
|
+
# @return [void]
|
15
|
+
def serialize( builder )
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :resource
|
21
|
+
|
22
|
+
class << self
|
23
|
+
|
24
|
+
# Find a {Serializer} that knows how to serialize the given resource.
|
25
|
+
# @param [Object] resource to serialize.
|
26
|
+
# @return [Serializer]
|
27
|
+
def find( resource )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Shamu
|
2
|
+
# {include:file:lib/shamu/json_api/README.md}
|
3
|
+
module JsonApi
|
4
|
+
require "shamu/json_api/context"
|
5
|
+
require "shamu/json_api/relationship_builder"
|
6
|
+
require "shamu/json_api/resource_builder"
|
7
|
+
require "shamu/json_api/response"
|
8
|
+
require "shamu/json_api/serializer"
|
9
|
+
require "shamu/json_api/support"
|
10
|
+
require "shamu/json_api/error"
|
11
|
+
require "shamu/json_api/error_builder"
|
12
|
+
end
|
13
|
+
end
|
data/lib/shamu/locale/en.yml
CHANGED
@@ -24,4 +24,9 @@ en:
|
|
24
24
|
|
25
25
|
features:
|
26
26
|
errors:
|
27
|
-
retired_toggle_checked: "The `%{name}` toggle retired at `%{retire_at}` and cannot be checked anymore."
|
27
|
+
retired_toggle_checked: "The `%{name}` toggle retired at `%{retire_at}` and cannot be checked anymore."
|
28
|
+
|
29
|
+
json_api:
|
30
|
+
errors:
|
31
|
+
incomplete_resource: "`identifier` was not called to define the type and id of the resource"
|
32
|
+
identifier_resource: "`identifier` must be called before defining any fields"
|
data/lib/shamu/version.rb
CHANGED
data/lib/shamu.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Shamu::JsonApi::BaseBuilder do
|
4
|
+
let( :context ) { Shamu::JsonApi::Context.new }
|
5
|
+
let( :builder ) { Shamu::JsonApi::BaseBuilder.new( context ) }
|
6
|
+
|
7
|
+
before( :each ) do
|
8
|
+
builder.identifier "example", 1
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#identifier" do
|
12
|
+
it "writes type and id" do
|
13
|
+
builder.identifier "spec", 5
|
14
|
+
|
15
|
+
expect( builder.compile ).to include type: "spec", id: "5"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#link" do
|
20
|
+
it "adds a link" do
|
21
|
+
builder.link :self, "http://localhost"
|
22
|
+
|
23
|
+
expect( builder.compile ).to include links: { self: "http://localhost" }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#meta" do
|
28
|
+
it "adds the meta data" do
|
29
|
+
builder.meta :updated, "today"
|
30
|
+
|
31
|
+
expect( builder.compile ).to include meta: { updated: "today" }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#compile" do
|
36
|
+
it "fails if identifier has not been specified" do
|
37
|
+
expect do
|
38
|
+
Shamu::JsonApi::BaseBuilder.new( context ).compile
|
39
|
+
end.to raise_error Shamu::JsonApi::IncompleteResourceError
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
File without changes
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Shamu::JsonApi::Context do
|
4
|
+
it "parses comma deliminated fields" do
|
5
|
+
context = Shamu::JsonApi::Context.new fields: { "user" => "name, email," }
|
6
|
+
|
7
|
+
expect( context.send( :fields ) ).to eq user: [:name, :email]
|
8
|
+
end
|
9
|
+
|
10
|
+
it "accepts array of fields" do
|
11
|
+
context = Shamu::JsonApi::Context.new fields: { "user" => [ "name", "email" ] }
|
12
|
+
|
13
|
+
expect( context.send( :fields ) ).to eq user: [:name, :email]
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "#include_field?" do
|
17
|
+
let( :context ) { Shamu::JsonApi::Context.new( fields: { "user": "name,email" } ) }
|
18
|
+
|
19
|
+
it "is true for unfiltered" do
|
20
|
+
expect( context.include_field?( :order, :number ) ).to be_truthy
|
21
|
+
end
|
22
|
+
|
23
|
+
it "is true for filtered with field" do
|
24
|
+
expect( context.include_field?( :user, :name ) ).to be_truthy
|
25
|
+
end
|
26
|
+
|
27
|
+
it "is false for filtered without field" do
|
28
|
+
expect( context.include_field?( :user, :birthdate ) ).not_to be_truthy
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Shamu::JsonApi::ErrorBuilder do
|
4
|
+
let( :builder ) { Shamu::JsonApi::ErrorBuilder.new }
|
5
|
+
|
6
|
+
describe "#summary" do
|
7
|
+
it "infers title" do
|
8
|
+
builder.summary 422, :not_allowed
|
9
|
+
|
10
|
+
expect( builder.compile ).to include title: "Not Allowed"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#exception" do
|
15
|
+
before( :each ) do
|
16
|
+
builder.exception NotImplementedError.new( "Nope, we haven't done that yet" )
|
17
|
+
end
|
18
|
+
|
19
|
+
it "applies message to details" do
|
20
|
+
expect( builder.compile ).to include detail: "Nope, we haven't done that yet"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "applies class name as code" do
|
24
|
+
expect( builder.compile ).to include code: "not_implemented"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "applies class name as title" do
|
28
|
+
expect( builder.compile ).to include title: "Not Implemented"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Shamu::JsonApi::RelationshipBuilder do
|
4
|
+
let( :context ) { Shamu::JsonApi::Context.new }
|
5
|
+
let( :builder ) { Shamu::JsonApi::ResourceBuilder.new( context ) }
|
6
|
+
|
7
|
+
before( :each ) do
|
8
|
+
builder.identifier "example", 1
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#attribute" do
|
12
|
+
it "adds to the attributes node" do
|
13
|
+
builder.attribute name: "Jim"
|
14
|
+
|
15
|
+
expect( builder.compile ).to include attributes: { name: "Jim" }
|
16
|
+
end
|
17
|
+
|
18
|
+
it "adds a single name, value pair" do
|
19
|
+
builder.attribute :name, "Jim"
|
20
|
+
|
21
|
+
expect( builder.compile ).to include attributes: { name: "Jim" }
|
22
|
+
end
|
23
|
+
|
24
|
+
it "excludes filtered attributes" do
|
25
|
+
allow( context ).to receive( :include_field? ).and_return false
|
26
|
+
|
27
|
+
builder.attribute name: "Nope"
|
28
|
+
expect( builder.compile ).not_to include attributes: { name: "Nope" }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#relationship" do
|
33
|
+
it "adds a relationship" do
|
34
|
+
builder.relationship :parent do |rel|
|
35
|
+
rel.identifier :example, 5
|
36
|
+
end
|
37
|
+
|
38
|
+
expect( builder.compile ).to include relationships: { parent: kind_of( Hash ) }
|
39
|
+
end
|
40
|
+
|
41
|
+
it "excludes filtered relationships" do
|
42
|
+
allow( context ).to receive( :include_field? ).and_return false
|
43
|
+
|
44
|
+
builder.relationship :parent do |rel|
|
45
|
+
rel.identifier :example, 5
|
46
|
+
end
|
47
|
+
|
48
|
+
expect( builder.compile ).not_to include relationships: { parent: kind_of( Hash ) }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Shamu::JsonApi::Response do
|
4
|
+
let( :context ) { Shamu::JsonApi::Context.new }
|
5
|
+
let( :response ) { Shamu::JsonApi::Response.new context }
|
6
|
+
|
7
|
+
it "uses serializer if given" do
|
8
|
+
serializer = double Shamu::JsonApi::Serializer
|
9
|
+
expect( serializer ).to receive( :serialize ) do |builder|
|
10
|
+
builder.identifier :response, 9
|
11
|
+
end
|
12
|
+
|
13
|
+
response.resource double, serializer
|
14
|
+
end
|
15
|
+
|
16
|
+
it "expects a block if no serializer" do
|
17
|
+
expect do
|
18
|
+
response.resource double
|
19
|
+
end.to raise_error /block/
|
20
|
+
end
|
21
|
+
|
22
|
+
it "appends included resources" do
|
23
|
+
|
24
|
+
response.resource double do |builder|
|
25
|
+
builder.identifier :example, 4
|
26
|
+
builder.relationship :parent do |rel|
|
27
|
+
rel.identifier :suite, 10
|
28
|
+
rel.include_resource double do |res|
|
29
|
+
res.identifier :suite, 10
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
expect( response.compile ).to include included: [ hash_including( type: "suite" ) ]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "includes errors" do
|
38
|
+
response.error NotImplementedError.new
|
39
|
+
|
40
|
+
expect( response.compile ).to include errors: [ hash_including( code: "not_implemented" ) ]
|
41
|
+
end
|
42
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shamu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Alexander
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -269,6 +269,18 @@ files:
|
|
269
269
|
- lib/shamu/features/support.rb
|
270
270
|
- lib/shamu/features/toggle.rb
|
271
271
|
- lib/shamu/features/toggle_codec.rb
|
272
|
+
- lib/shamu/json_api.rb
|
273
|
+
- lib/shamu/json_api/README.md
|
274
|
+
- lib/shamu/json_api/base_builder.rb
|
275
|
+
- lib/shamu/json_api/context.rb
|
276
|
+
- lib/shamu/json_api/entity_serializer.rb
|
277
|
+
- lib/shamu/json_api/error.rb
|
278
|
+
- lib/shamu/json_api/error_builder.rb
|
279
|
+
- lib/shamu/json_api/relationship_builder.rb
|
280
|
+
- lib/shamu/json_api/resource_builder.rb
|
281
|
+
- lib/shamu/json_api/response.rb
|
282
|
+
- lib/shamu/json_api/serializer.rb
|
283
|
+
- lib/shamu/json_api/support.rb
|
272
284
|
- lib/shamu/locale/en.yml
|
273
285
|
- lib/shamu/logger.rb
|
274
286
|
- lib/shamu/rack.rb
|
@@ -363,6 +375,13 @@ files:
|
|
363
375
|
- spec/lib/shamu/features/support_spec.rb
|
364
376
|
- spec/lib/shamu/features/toggle_codec_spec.rb
|
365
377
|
- spec/lib/shamu/features/toggle_spec.rb
|
378
|
+
- spec/lib/shamu/json_api/base_builder_spec.rb
|
379
|
+
- spec/lib/shamu/json_api/common_builder_spec.rb
|
380
|
+
- spec/lib/shamu/json_api/context_spec.rb
|
381
|
+
- spec/lib/shamu/json_api/error_builder_spec.rb
|
382
|
+
- spec/lib/shamu/json_api/relationship_builder_spec.rb
|
383
|
+
- spec/lib/shamu/json_api/resource_builder_spec.rb
|
384
|
+
- spec/lib/shamu/json_api/response_spec.rb
|
366
385
|
- spec/lib/shamu/rack/cookies_middleware_spec.rb
|
367
386
|
- spec/lib/shamu/rack/cookies_spec.rb
|
368
387
|
- spec/lib/shamu/rack/query_params_middleware_spec.rb
|
@@ -466,6 +485,13 @@ test_files:
|
|
466
485
|
- spec/lib/shamu/features/support_spec.rb
|
467
486
|
- spec/lib/shamu/features/toggle_codec_spec.rb
|
468
487
|
- spec/lib/shamu/features/toggle_spec.rb
|
488
|
+
- spec/lib/shamu/json_api/base_builder_spec.rb
|
489
|
+
- spec/lib/shamu/json_api/common_builder_spec.rb
|
490
|
+
- spec/lib/shamu/json_api/context_spec.rb
|
491
|
+
- spec/lib/shamu/json_api/error_builder_spec.rb
|
492
|
+
- spec/lib/shamu/json_api/relationship_builder_spec.rb
|
493
|
+
- spec/lib/shamu/json_api/resource_builder_spec.rb
|
494
|
+
- spec/lib/shamu/json_api/response_spec.rb
|
469
495
|
- spec/lib/shamu/rack/cookies_middleware_spec.rb
|
470
496
|
- spec/lib/shamu/rack/cookies_spec.rb
|
471
497
|
- spec/lib/shamu/rack/query_params_middleware_spec.rb
|