simple_json_api 0.0.2
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 +15 -0
- data/.overcommit.yml +31 -0
- data/.rubocop.yml +8 -0
- data/.travis.yml +11 -0
- data/Gemfile +21 -0
- data/LICENSE +22 -0
- data/LICENSE.txt +22 -0
- data/README.md +37 -0
- data/Rakefile +22 -0
- data/lib/simple_json_api.rb +27 -0
- data/lib/simple_json_api/active_record_refinements.rb +21 -0
- data/lib/simple_json_api/array_refinements.rb +14 -0
- data/lib/simple_json_api/array_serializer.rb +16 -0
- data/lib/simple_json_api/association.rb +10 -0
- data/lib/simple_json_api/attribute.rb +9 -0
- data/lib/simple_json_api/dsl.rb +61 -0
- data/lib/simple_json_api/field_list.rb +33 -0
- data/lib/simple_json_api/include_list.rb +20 -0
- data/lib/simple_json_api/json_api_builder.rb +92 -0
- data/lib/simple_json_api/resource_serializer.rb +68 -0
- data/lib/simple_json_api/serializer.rb +52 -0
- data/lib/simple_json_api/version.rb +4 -0
- data/simple_json_api.gemspec +34 -0
- data/test/fixtures/projects.yml +5 -0
- data/test/integration/render_basic_test.rb +71 -0
- data/test/integration/render_fields_test.rb +78 -0
- data/test/integration/render_include_test.rb +133 -0
- data/test/integration/render_nested_include_test.rb +116 -0
- data/test/integration/serializers_test.rb +41 -0
- data/test/setup/data.rb +24 -0
- data/test/setup/models.rb +49 -0
- data/test/setup/schema.rb +38 -0
- data/test/setup/serializers.rb +57 -0
- data/test/test_helper.rb +33 -0
- data/test/test_setup.rb +19 -0
- data/test/tmp/schema.rb +47 -0
- data/test/unit/association_test.rb +28 -0
- data/test/unit/attribute_test.rb +22 -0
- data/test/unit/field_list_test.rb +28 -0
- data/test/unit/include_list_test.rb +23 -0
- metadata +241 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
# SimpleJsonApi
|
2
|
+
module SimpleJsonApi
|
3
|
+
# The Serializer will serialize a model
|
4
|
+
class ResourceSerializer < Serializer
|
5
|
+
using ActiveRecordRefinements
|
6
|
+
|
7
|
+
def _root_name
|
8
|
+
self.class._root_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def _fields
|
12
|
+
@_fields ||= begin
|
13
|
+
defaults = self.class.default_fields.split(',')
|
14
|
+
builders = _builder.fields_for(_root_name).presence
|
15
|
+
(builders || defaults).map(&:to_sym)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def serialize
|
20
|
+
hash = {}
|
21
|
+
attribute_values(hash)
|
22
|
+
link_values(hash[:links] = {})
|
23
|
+
_association_values
|
24
|
+
hash
|
25
|
+
end
|
26
|
+
|
27
|
+
def attribute_values(hash)
|
28
|
+
_fields.each do |attribute, _attr_opts|
|
29
|
+
hash[attribute] = send(attribute).to_s
|
30
|
+
end
|
31
|
+
hash[:href] = href if self.class.method_defined? :href
|
32
|
+
end
|
33
|
+
|
34
|
+
def link_values(root)
|
35
|
+
self.class._associations.each do |association|
|
36
|
+
root[association.key] = link(association)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def link(association)
|
41
|
+
resource = send(association.name)
|
42
|
+
if association.type == :has_many
|
43
|
+
resource.to_a.map do |obj|
|
44
|
+
association[:polymorphic] ? obj.typed_json_id : obj.json_id
|
45
|
+
end
|
46
|
+
else
|
47
|
+
resource.json_id
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def _associations
|
52
|
+
self.class._associations
|
53
|
+
end
|
54
|
+
|
55
|
+
def _association_values
|
56
|
+
_associations.each do |association|
|
57
|
+
name = association[:name]
|
58
|
+
obj = send(name)
|
59
|
+
_builder.add_linked_elem(
|
60
|
+
name,
|
61
|
+
obj,
|
62
|
+
association,
|
63
|
+
@_base
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# SimpleJsonApi
|
2
|
+
module SimpleJsonApi
|
3
|
+
# The Serializer will serialize a model
|
4
|
+
class Serializer
|
5
|
+
extend Forwardable
|
6
|
+
extend DSL
|
7
|
+
|
8
|
+
attr_reader :_builder
|
9
|
+
attr_reader :_each_serializer
|
10
|
+
attr_reader :_object
|
11
|
+
|
12
|
+
def initialize(object:, builder:, each_serializer: nil, base: nil)
|
13
|
+
@_object = object
|
14
|
+
@_builder = builder
|
15
|
+
@_each_serializer = each_serializer
|
16
|
+
@_base = base
|
17
|
+
end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
attr_reader :registered_serializers
|
21
|
+
|
22
|
+
def register_serializer(serializer_hash, options)
|
23
|
+
options.reverse_merge!(primary_key: :id)
|
24
|
+
@registered_serializers ||= []
|
25
|
+
@registered_serializers << serializer_hash.merge(options)
|
26
|
+
end
|
27
|
+
|
28
|
+
def for(model, association)
|
29
|
+
if association[:polymorphic]
|
30
|
+
for_polymorphic(model)
|
31
|
+
else
|
32
|
+
for_regular(association)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def for_regular(association)
|
37
|
+
resource = association[:name].to_s.pluralize.to_sym
|
38
|
+
serializer = @registered_serializers.find do |entry|
|
39
|
+
entry[:resource] == resource
|
40
|
+
end
|
41
|
+
serializer[:serializer]
|
42
|
+
end
|
43
|
+
|
44
|
+
def for_polymorphic(model)
|
45
|
+
serializer = @registered_serializers.find do |entry|
|
46
|
+
entry[:model] == model.class
|
47
|
+
end
|
48
|
+
serializer[:serializer]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'simple_json_api/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'simple_json_api'
|
8
|
+
spec.version = SimpleJsonApi::VERSION
|
9
|
+
spec.authors = ['Gary Gordon']
|
10
|
+
spec.email = ['gfgordon@gmail.com']
|
11
|
+
spec.summary = 'A Simple JSON API Serializer.'
|
12
|
+
spec.description = 'A basic serializer implementing the JSON API spec.'
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(/^test\//)
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
spec.required_ruby_version = '~> 2.1'
|
21
|
+
|
22
|
+
spec.add_dependency 'activerecord', '>= 4.0'
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.7'
|
25
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
26
|
+
spec.add_development_dependency 'rdoc'
|
27
|
+
|
28
|
+
spec.add_development_dependency 'awesome_print'
|
29
|
+
spec.add_development_dependency 'diffy'
|
30
|
+
spec.add_development_dependency 'minitest'
|
31
|
+
spec.add_development_dependency 'minitest-reporters'
|
32
|
+
spec.add_development_dependency 'simplecov'
|
33
|
+
spec.add_development_dependency 'sqlite3'
|
34
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
# SimpleJsonApi
|
4
|
+
module SimpleJsonApi
|
5
|
+
describe 'RenderBasicTest' do
|
6
|
+
it 'should match json hash for a project with no options' do
|
7
|
+
JSON.parse(actual_project).must_equal expected_project
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should match json hash for a project array with no options' do
|
11
|
+
JSON.parse(actual_projects).must_equal expected_projects
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:actual_project) do
|
15
|
+
SimpleJsonApi.render(
|
16
|
+
model: Project.first,
|
17
|
+
serializer: ProjectSerializer
|
18
|
+
)
|
19
|
+
end
|
20
|
+
let(:expected_project) do
|
21
|
+
{
|
22
|
+
'projects' => {
|
23
|
+
'id' => '100',
|
24
|
+
'name' => 'First Project',
|
25
|
+
'description' => 'The first project',
|
26
|
+
'position' => '',
|
27
|
+
'href' => 'http://example.com/projects/100',
|
28
|
+
'links' => {
|
29
|
+
'todolist' => '200',
|
30
|
+
'tags' => ['10']
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:actual_projects) do
|
37
|
+
SimpleJsonApi.render(
|
38
|
+
model: Project.all.to_a,
|
39
|
+
serializer: ProjectSerializer
|
40
|
+
)
|
41
|
+
end
|
42
|
+
let(:expected_projects) do
|
43
|
+
{
|
44
|
+
'projects' => [
|
45
|
+
{
|
46
|
+
'id' => '100',
|
47
|
+
'name' => 'First Project',
|
48
|
+
'description' => 'The first project',
|
49
|
+
'position' => '',
|
50
|
+
'href' => 'http://example.com/projects/100',
|
51
|
+
'links' => {
|
52
|
+
'todolist' => '200',
|
53
|
+
'tags' => ['10']
|
54
|
+
}
|
55
|
+
},
|
56
|
+
{
|
57
|
+
'id' => '110',
|
58
|
+
'name' => 'Second Project',
|
59
|
+
'description' => 'The second project',
|
60
|
+
'position' => '',
|
61
|
+
'href' => 'http://example.com/projects/110',
|
62
|
+
'links' => {
|
63
|
+
'todolist' => '210',
|
64
|
+
'tags' => ['20']
|
65
|
+
}
|
66
|
+
}
|
67
|
+
]
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
# SimpleJsonApi
|
4
|
+
module SimpleJsonApi
|
5
|
+
describe 'RenderFieldsTest' do
|
6
|
+
it 'should match json hash for a project with fields specified' do
|
7
|
+
JSON.parse(actual_project).must_equal expected_project
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should match json hash for a project with fields specified as hash' do
|
11
|
+
JSON.parse(actual_project_field_hash).must_equal expected_project
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should match json hash for a project array with fields specified' do
|
15
|
+
JSON.parse(actual_projects).must_equal expected_projects
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:actual_project) do
|
19
|
+
SimpleJsonApi.render(
|
20
|
+
model: Project.first,
|
21
|
+
serializer: ProjectSerializer,
|
22
|
+
options: { fields: 'name,description' }
|
23
|
+
)
|
24
|
+
end
|
25
|
+
let(:actual_project_field_hash) do
|
26
|
+
SimpleJsonApi.render(
|
27
|
+
model: Project.first,
|
28
|
+
serializer: ProjectSerializer,
|
29
|
+
options: { fields: { projects: 'name,description' } }
|
30
|
+
)
|
31
|
+
end
|
32
|
+
let(:expected_project) do
|
33
|
+
{
|
34
|
+
'projects' => {
|
35
|
+
'name' => 'First Project',
|
36
|
+
'description' => 'The first project',
|
37
|
+
'href' => 'http://example.com/projects/100',
|
38
|
+
'links' => {
|
39
|
+
'todolist' => '200',
|
40
|
+
'tags' => ['10']
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:actual_projects) do
|
47
|
+
SimpleJsonApi.render(
|
48
|
+
model: Project.all.to_a,
|
49
|
+
serializer: ProjectSerializer,
|
50
|
+
options: { fields: 'name,description' }
|
51
|
+
)
|
52
|
+
end
|
53
|
+
let(:expected_projects) do
|
54
|
+
{
|
55
|
+
'projects' => [
|
56
|
+
{
|
57
|
+
'name' => 'First Project',
|
58
|
+
'description' => 'The first project',
|
59
|
+
'href' => 'http://example.com/projects/100',
|
60
|
+
'links' => {
|
61
|
+
'todolist' => '200',
|
62
|
+
'tags' => ['10']
|
63
|
+
}
|
64
|
+
},
|
65
|
+
{
|
66
|
+
'name' => 'Second Project',
|
67
|
+
'description' => 'The second project',
|
68
|
+
'href' => 'http://example.com/projects/110',
|
69
|
+
'links' => {
|
70
|
+
'todolist' => '210',
|
71
|
+
'tags' => ['20']
|
72
|
+
}
|
73
|
+
}
|
74
|
+
]
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
# SimpleJsonApi
|
4
|
+
module SimpleJsonApi
|
5
|
+
describe 'RenderIncludeTest' do
|
6
|
+
it 'should match json hash for a project with include specified' do
|
7
|
+
JSON.parse(actual_project).must_equal expected_project
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should match json hash for a project array with include specified' do
|
11
|
+
JSON.parse(actual_projects).must_equal expected_projects
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:actual_project) do
|
15
|
+
SimpleJsonApi.render(
|
16
|
+
model: Project.first,
|
17
|
+
serializer: ProjectSerializer,
|
18
|
+
options: { include: 'todolists' }
|
19
|
+
)
|
20
|
+
end
|
21
|
+
let(:expected_project) do
|
22
|
+
{
|
23
|
+
'projects' => {
|
24
|
+
'id' => '100',
|
25
|
+
'name' => 'First Project',
|
26
|
+
'description' => 'The first project',
|
27
|
+
'position' => '',
|
28
|
+
'href' => 'http://example.com/projects/100',
|
29
|
+
'links' => {
|
30
|
+
'todolist' => '200',
|
31
|
+
'tags' => ['10']
|
32
|
+
}
|
33
|
+
},
|
34
|
+
'linked' => {
|
35
|
+
'todolists' => [
|
36
|
+
{
|
37
|
+
'id' => '200',
|
38
|
+
'description' => 'Groceries',
|
39
|
+
'href' => 'http://example.com/todolists/200',
|
40
|
+
'links' => {
|
41
|
+
'todos' => ['300'],
|
42
|
+
'tags' => ['30']
|
43
|
+
}
|
44
|
+
}
|
45
|
+
]
|
46
|
+
}
|
47
|
+
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
let(:actual_projects) do
|
52
|
+
SimpleJsonApi.render(
|
53
|
+
model: Project.all.to_a,
|
54
|
+
serializer: ProjectSerializer,
|
55
|
+
options: { include: 'todolists,tags' }
|
56
|
+
)
|
57
|
+
end
|
58
|
+
let(:expected_projects) do
|
59
|
+
{
|
60
|
+
'projects' => [
|
61
|
+
{
|
62
|
+
'id' => '100',
|
63
|
+
'name' => 'First Project',
|
64
|
+
'description' => 'The first project',
|
65
|
+
'position' => '',
|
66
|
+
'href' => 'http://example.com/projects/100',
|
67
|
+
'links' => {
|
68
|
+
'todolist' => '200',
|
69
|
+
'tags' => ['10']
|
70
|
+
}
|
71
|
+
},
|
72
|
+
{
|
73
|
+
'id' => '110',
|
74
|
+
'name' => 'Second Project',
|
75
|
+
'description' => 'The second project',
|
76
|
+
'position' => '',
|
77
|
+
'href' => 'http://example.com/projects/110',
|
78
|
+
'links' => {
|
79
|
+
'todolist' => '210',
|
80
|
+
'tags' => ['20']
|
81
|
+
}
|
82
|
+
}
|
83
|
+
],
|
84
|
+
'linked' => {
|
85
|
+
'todolists' => [
|
86
|
+
{
|
87
|
+
'id' => '200',
|
88
|
+
'description' => 'Groceries',
|
89
|
+
'href' => 'http://example.com/todolists/200',
|
90
|
+
'links' => {
|
91
|
+
'todos' => ['300'],
|
92
|
+
'tags' => ['30']
|
93
|
+
}
|
94
|
+
},
|
95
|
+
{
|
96
|
+
'id' => '210',
|
97
|
+
'description' => 'Groceries',
|
98
|
+
'description' => 'Work',
|
99
|
+
'href' => 'http://example.com/todolists/210',
|
100
|
+
'links' => {
|
101
|
+
'todos' => %w(310 320 330),
|
102
|
+
'tags' => []
|
103
|
+
}
|
104
|
+
}
|
105
|
+
],
|
106
|
+
'tags' => [
|
107
|
+
{
|
108
|
+
'guid' => '10',
|
109
|
+
'name' => 'Urgent!',
|
110
|
+
'links' => {
|
111
|
+
'taggables' => [
|
112
|
+
%w(projects 100),
|
113
|
+
%w(todos 300),
|
114
|
+
%w(todos 330)
|
115
|
+
]
|
116
|
+
}
|
117
|
+
},
|
118
|
+
{
|
119
|
+
'guid' => '20',
|
120
|
+
'name' => 'On Hold',
|
121
|
+
'links' => {
|
122
|
+
'taggables' => [
|
123
|
+
%w(projects 110),
|
124
|
+
%w(todos 300)
|
125
|
+
]
|
126
|
+
}
|
127
|
+
}
|
128
|
+
]
|
129
|
+
}
|
130
|
+
}
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|