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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.overcommit.yml +31 -0
  4. data/.rubocop.yml +8 -0
  5. data/.travis.yml +11 -0
  6. data/Gemfile +21 -0
  7. data/LICENSE +22 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +37 -0
  10. data/Rakefile +22 -0
  11. data/lib/simple_json_api.rb +27 -0
  12. data/lib/simple_json_api/active_record_refinements.rb +21 -0
  13. data/lib/simple_json_api/array_refinements.rb +14 -0
  14. data/lib/simple_json_api/array_serializer.rb +16 -0
  15. data/lib/simple_json_api/association.rb +10 -0
  16. data/lib/simple_json_api/attribute.rb +9 -0
  17. data/lib/simple_json_api/dsl.rb +61 -0
  18. data/lib/simple_json_api/field_list.rb +33 -0
  19. data/lib/simple_json_api/include_list.rb +20 -0
  20. data/lib/simple_json_api/json_api_builder.rb +92 -0
  21. data/lib/simple_json_api/resource_serializer.rb +68 -0
  22. data/lib/simple_json_api/serializer.rb +52 -0
  23. data/lib/simple_json_api/version.rb +4 -0
  24. data/simple_json_api.gemspec +34 -0
  25. data/test/fixtures/projects.yml +5 -0
  26. data/test/integration/render_basic_test.rb +71 -0
  27. data/test/integration/render_fields_test.rb +78 -0
  28. data/test/integration/render_include_test.rb +133 -0
  29. data/test/integration/render_nested_include_test.rb +116 -0
  30. data/test/integration/serializers_test.rb +41 -0
  31. data/test/setup/data.rb +24 -0
  32. data/test/setup/models.rb +49 -0
  33. data/test/setup/schema.rb +38 -0
  34. data/test/setup/serializers.rb +57 -0
  35. data/test/test_helper.rb +33 -0
  36. data/test/test_setup.rb +19 -0
  37. data/test/tmp/schema.rb +47 -0
  38. data/test/unit/association_test.rb +28 -0
  39. data/test/unit/attribute_test.rb +22 -0
  40. data/test/unit/field_list_test.rb +28 -0
  41. data/test/unit/include_list_test.rb +23 -0
  42. 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,4 @@
1
+ # SimpleJsonApi
2
+ module SimpleJsonApi
3
+ VERSION = '0.0.2'
4
+ 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,5 @@
1
+ project_1:
2
+ name: First Project
3
+
4
+ project_2:
5
+ name: Second Project
@@ -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