simple_json_api 0.0.3 → 0.0.4
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 +4 -4
- data/Gemfile +14 -2
- data/README.md +1 -1
- data/lib/generators/simple_json_api/resource/USAGE +8 -0
- data/lib/generators/simple_json_api/resource/resource_generator.rb +73 -0
- data/lib/generators/simple_json_api/resource/templates/controller_template.rb.erb +34 -0
- data/lib/generators/simple_json_api/resource/templates/serializer_template.rb.erb +14 -0
- data/lib/simple_json_api/api_node.rb +68 -0
- data/lib/simple_json_api/array_serializer.rb +15 -1
- data/lib/simple_json_api/association.rb +16 -0
- data/lib/simple_json_api/attribute.rb +9 -0
- data/lib/simple_json_api/builder.rb +35 -0
- data/lib/simple_json_api/dsl.rb +35 -5
- data/lib/simple_json_api/field_list.rb +2 -7
- data/lib/simple_json_api/helper.rb +5 -0
- data/lib/simple_json_api/include_list.rb +12 -7
- data/lib/simple_json_api/json_api_wrapper.rb +46 -0
- data/lib/simple_json_api/member/data.rb +14 -0
- data/lib/simple_json_api/member/included.rb +38 -0
- data/lib/simple_json_api/member/links.rb +9 -0
- data/lib/simple_json_api/member/meta.rb +9 -0
- data/lib/simple_json_api/refinements/active_record.rb +23 -0
- data/lib/simple_json_api/refinements/array.rb +13 -0
- data/lib/simple_json_api/refinements/symbol.rb +17 -0
- data/lib/simple_json_api/resource.rb +23 -0
- data/lib/simple_json_api/resource_serializer.rb +25 -24
- data/lib/simple_json_api/serializer.rb +39 -2
- data/lib/simple_json_api/serializer_factory.rb +22 -0
- data/lib/simple_json_api/version.rb +1 -1
- data/lib/simple_json_api.rb +7 -29
- data/simple_json_api.gemspec +2 -7
- data/test/generators/simple_json_api/resource_generator_test.rb +31 -0
- data/test/integration/render_basic_test.rb +10 -7
- data/test/integration/render_fields_test.rb +35 -16
- data/test/integration/render_include_test.rb +66 -63
- data/test/integration/render_nested_include_test.rb +38 -83
- data/test/integration/serializers_test.rb +1 -1
- data/test/setup/data.rb +7 -3
- data/test/setup/serializers.rb +2 -0
- data/test/test_helper.rb +3 -9
- data/test/unit/field_list_test.rb +0 -10
- data/test/unit/include_list_test.rb +35 -5
- data/test/unit/symbol_refinement_test.rb +16 -0
- metadata +25 -89
- data/lib/simple_json_api/active_record_refinements.rb +0 -21
- data/lib/simple_json_api/array_refinements.rb +0 -14
- data/lib/simple_json_api/json_api_builder.rb +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d342c421da25b887edeade3d14b1a145bbe3a352
|
4
|
+
data.tar.gz: 2720735c97db2a23d45ac6154460f2c92f8dfa67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87165f1029afae88c79cae12d06d502ad77fe55c6d2a68cdb02b6446ef757b73cc2ea3e0906418083ba1f358f5a79a88a7348acdb8754227e6d24c65f9182038
|
7
|
+
data.tar.gz: 7f0428e1ab60247971c0ef04ad4a09a156ab62c5c784473024c90da6809ec6656fd77b006025593aa3b021852219830f585cbd004e401544a07a6c905fd2e825
|
data/Gemfile
CHANGED
@@ -8,7 +8,6 @@ version = ENV['RAILS_VERSION'] || '4.1'
|
|
8
8
|
case version
|
9
9
|
when 'master'
|
10
10
|
gem 'rails', github: 'rails/rails'
|
11
|
-
|
12
11
|
# Learned from AMS
|
13
12
|
# ugh https://github.com/rails/rails/issues/16063#issuecomment-48090125
|
14
13
|
gem 'arel', github: 'rails/arel'
|
@@ -18,4 +17,17 @@ else
|
|
18
17
|
fail GemfileError, "Unsupported Rails version - #{version}"
|
19
18
|
end
|
20
19
|
|
21
|
-
|
20
|
+
group :test do
|
21
|
+
gem 'codeclimate-test-reporter', require: nil
|
22
|
+
end
|
23
|
+
|
24
|
+
group :development do
|
25
|
+
gem 'rdoc'
|
26
|
+
|
27
|
+
gem 'awesome_print'
|
28
|
+
gem 'minitest-match_json'
|
29
|
+
gem 'diffy'
|
30
|
+
gem 'minitest'
|
31
|
+
gem 'minitest-reporters'
|
32
|
+
gem 'sqlite3'
|
33
|
+
end
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# SimpleJsonApi
|
2
2
|
|
3
|
-
[](https://travis-ci.org/ggordon/simple_json_api) [](https://codeclimate.com/github/ggordon/simple_json_api) [](https://codeclimate.com/github/ggordon/simple_json_api)
|
3
|
+
[](https://travis-ci.org/ggordon/simple_json_api) [](https://codeclimate.com/github/ggordon/simple_json_api) [](https://codeclimate.com/github/ggordon/simple_json_api) [](http://badge.fury.io/rb/simple_json_api)
|
4
4
|
|
5
5
|
A gem to render json following the [jsonapi](http://jsonapi.org) spec.
|
6
6
|
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module SimpleJsonApi
|
2
|
+
module Generators
|
3
|
+
# Generates the resource template files
|
4
|
+
class ResourceGenerator < Rails::Generators::NamedBase
|
5
|
+
source_root File.expand_path('../templates', __FILE__)
|
6
|
+
|
7
|
+
argument :name, type: :string, required: true, banner: 'ResourceName'
|
8
|
+
|
9
|
+
class_option :model,
|
10
|
+
desc: 'Model class if different than Resource',
|
11
|
+
type: :string
|
12
|
+
class_option :namespace,
|
13
|
+
desc: 'Namespace for the generated files',
|
14
|
+
type: :string
|
15
|
+
class_option :controller,
|
16
|
+
desc: 'Base controller for resources',
|
17
|
+
type: :string
|
18
|
+
class_option :skip_serializer,
|
19
|
+
desc: "Don't generate a serializer file.",
|
20
|
+
type: :boolean
|
21
|
+
class_option :skip_controller,
|
22
|
+
desc: "Don't generate a controller file.",
|
23
|
+
type: :boolean
|
24
|
+
class_option :skip_service,
|
25
|
+
desc: "Don't generate a service file.",
|
26
|
+
type: :boolean
|
27
|
+
class_option :root_dir,
|
28
|
+
desc: "Root dir for generated code, default: '.'.",
|
29
|
+
type: :string
|
30
|
+
|
31
|
+
def create_resource
|
32
|
+
@namespace = options[:namespace]
|
33
|
+
@model = options[:model] || class_name
|
34
|
+
namespaced_name = [@namespace, class_name].compact.join('::')
|
35
|
+
@serializer_name = "#{namespaced_name}Serializer"
|
36
|
+
@controller_name = "#{namespaced_name.pluralize}Controller"
|
37
|
+
@base_controller = options[:controller] || 'ApplicationController'
|
38
|
+
@root_dir = options[:root_dir] || '.'
|
39
|
+
file_path = "#{@namespace.underscore}/#{class_name.underscore}"
|
40
|
+
|
41
|
+
check_model
|
42
|
+
|
43
|
+
unless options[:skip_serializer]
|
44
|
+
template 'serializer_template.rb.erb',
|
45
|
+
"#{@root_dir}/app/serializers/#{file_path}_serializer.rb"
|
46
|
+
# TODO: create serializer test
|
47
|
+
end
|
48
|
+
|
49
|
+
unless options[:skip_controller]
|
50
|
+
template 'controller_template.rb.erb',
|
51
|
+
"#{@root_dir}/app/controllers/#{file_path.pluralize}_controller.rb"
|
52
|
+
# TODO: create controller test
|
53
|
+
end
|
54
|
+
|
55
|
+
unless options[:skip_service]
|
56
|
+
# TODO: create service
|
57
|
+
# TODO: create service test
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def check_model
|
64
|
+
unless @model.constantize.ancestors.include?(ActiveRecord::Base)
|
65
|
+
fail NameError
|
66
|
+
end
|
67
|
+
rescue NameError
|
68
|
+
puts "Error: '#{@model}' is not an AR model"
|
69
|
+
exit
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class <%= @controller_name -%> < <%= @base_controller %>
|
2
|
+
include SimpleJsonApi::Helper
|
3
|
+
|
4
|
+
def index
|
5
|
+
@<%= class_name.underscore.gsub('/', '__').pluralize %> = <%= @model -%>.paginate(page: params[:page], per_page: params[:per_page]).to_a
|
6
|
+
render json: SimpleJsonApi.render(
|
7
|
+
model: @<%= class_name.underscore.gsub('/', '__').pluralize %>,
|
8
|
+
serializer: <%= @serializer_name %>,
|
9
|
+
fields: options_hash.fetch(:fields, nil),
|
10
|
+
include: options_hash.fetch(:include, nil)
|
11
|
+
),
|
12
|
+
status: 200
|
13
|
+
end
|
14
|
+
|
15
|
+
def show
|
16
|
+
@<%= class_name.underscore.gsub('/', '__') %> = <%= @model -%>.find(params[:id])
|
17
|
+
render json: SimpleJsonApi.render(
|
18
|
+
model: @<%= class_name.underscore.gsub('/', '__') %>,
|
19
|
+
serializer: <%= @serializer_name %>,
|
20
|
+
fields: options_hash.fetch(:fields, nil),
|
21
|
+
include: options_hash.fetch(:include, nil)
|
22
|
+
),
|
23
|
+
status: 200
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def options_hash
|
29
|
+
{
|
30
|
+
fields: params[:fields],
|
31
|
+
include: params[:include]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class <%= @serializer_name -%> < SimpleJsonApi::ResourceSerializer
|
2
|
+
serializes :<%= class_name.underscore.gsub('/', '__').pluralize %>, model: <%= "::#{@model}" -%>, primary_key: :<%= @model.constantize.primary_key %>
|
3
|
+
|
4
|
+
<%- @model.constantize.column_names.each do |col| -%>
|
5
|
+
attribute :<%= col %>
|
6
|
+
<%- end -%>
|
7
|
+
|
8
|
+
<%- @model.constantize.reflect_on_all_associations.each do |assoc| -%>
|
9
|
+
<%- klass_name = assoc.options[:class_name] -%>
|
10
|
+
<%- serializer = [@namespace, klass_name].compact.join('::') + 'Serializer' if klass_name -%>
|
11
|
+
<%= assoc.macro %> :<%= assoc.name %><%= ', polymorphic: true' if assoc.options[:polymorphic] %><%= ', serializer: ' + serializer if serializer %>
|
12
|
+
<%- end -%>
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'simple_json_api/refinements/symbol'
|
2
|
+
|
3
|
+
module SimpleJsonApi
|
4
|
+
# Node in the directed graph of associations (eventually)
|
5
|
+
# Will start as a tree with duplication
|
6
|
+
class ApiNode
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :serializer, :serialize
|
9
|
+
|
10
|
+
attr_reader :name
|
11
|
+
attr_reader :serializer
|
12
|
+
attr_reader :associations
|
13
|
+
attr_reader :assoc_list
|
14
|
+
|
15
|
+
def initialize(name, serializer, assoc_list, each_serializer = nil)
|
16
|
+
@name = name
|
17
|
+
@serializer = serializer
|
18
|
+
@each_serializer = each_serializer
|
19
|
+
@assoc_list = assoc_list
|
20
|
+
@associations = [] # list of api_nodes
|
21
|
+
end
|
22
|
+
|
23
|
+
def load
|
24
|
+
return self unless serializer_actual._associations
|
25
|
+
serializer_actual._associations.each do |association|
|
26
|
+
add_association(association)
|
27
|
+
end
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def serializer_actual
|
32
|
+
@serializer_actual ||= @each_serializer || self.serializer
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_association(association)
|
36
|
+
return unless @assoc_list.key? association.plural_name
|
37
|
+
resource = serializer.associated_object(association.name)
|
38
|
+
Array(resource).each do |object|
|
39
|
+
each_serializer = Serializer.for(object, association)
|
40
|
+
serializer = SerializerFactory.create(
|
41
|
+
object, each_serializer, self.serializer._builder
|
42
|
+
)
|
43
|
+
self <<
|
44
|
+
ApiNode.new(
|
45
|
+
association.plural_name,
|
46
|
+
serializer,
|
47
|
+
@assoc_list[association.plural_name],
|
48
|
+
serializer._each_serializer
|
49
|
+
).load
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def <<(node)
|
54
|
+
add_assoc(node)
|
55
|
+
end
|
56
|
+
|
57
|
+
def add_assoc(node)
|
58
|
+
@associations << node
|
59
|
+
end
|
60
|
+
|
61
|
+
# def display(offset = '')
|
62
|
+
# ap "DISPLAY: #{offset}#{@name}, #{@assoc_list}, #{Array(@object).first.class}, #{Array(@object).map(&:id)}"
|
63
|
+
# @associations.each do |assoc|
|
64
|
+
# assoc.display(offset + ' ')
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
end
|
68
|
+
end
|
@@ -5,12 +5,26 @@ module SimpleJsonApi
|
|
5
5
|
def serialize
|
6
6
|
_object.map do |object|
|
7
7
|
serializer = _each_serializer.new(object, _builder)
|
8
|
-
serializer.serialize
|
8
|
+
Resource.new(serializer.serialize)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
+
def associated_object(association_name)
|
13
|
+
serializers.map { |serializer| serializer.send(association_name) }
|
14
|
+
end
|
15
|
+
|
12
16
|
def _root_name
|
13
17
|
_each_serializer._root_name
|
14
18
|
end
|
19
|
+
|
20
|
+
def _associations
|
21
|
+
_each_serializer._associations if _each_serializer
|
22
|
+
end
|
23
|
+
|
24
|
+
def serializers
|
25
|
+
_object.map do |object|
|
26
|
+
_each_serializer.new(object, _builder)
|
27
|
+
end
|
28
|
+
end
|
15
29
|
end
|
16
30
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# SimpleJsonApi
|
2
|
+
module SimpleJsonApi
|
3
|
+
# Wrapper for an included association
|
4
|
+
Association = Struct.new(:name, :type, :serializer, :polymorphic, :key) do
|
5
|
+
using Refinements::Symbol
|
6
|
+
|
7
|
+
def key
|
8
|
+
name_s = name.to_s
|
9
|
+
(type == :has_many) ? name_s.pluralize.to_sym : name_s.singularize.to_sym
|
10
|
+
end
|
11
|
+
|
12
|
+
def plural_name
|
13
|
+
name.pluralize
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'simple_json_api/api_node'
|
2
|
+
require 'simple_json_api/field_list'
|
3
|
+
require 'simple_json_api/include_list'
|
4
|
+
require 'simple_json_api/refinements/active_record'
|
5
|
+
require 'simple_json_api/serializer_factory'
|
6
|
+
|
7
|
+
# SimpleJsonApi
|
8
|
+
module SimpleJsonApi
|
9
|
+
# The Builder to walk the hierarchy and construct the JSON
|
10
|
+
class Builder
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
attr_reader :wrapper
|
14
|
+
attr_reader :field_list
|
15
|
+
attr_reader :include
|
16
|
+
attr_reader :object
|
17
|
+
attr_reader :serializer
|
18
|
+
|
19
|
+
def_delegators :@field_list, :fields_for
|
20
|
+
|
21
|
+
# TODO: sort: nil
|
22
|
+
def initialize(object, wrapper, serializer, fields, include)
|
23
|
+
@object = object
|
24
|
+
@wrapper = wrapper
|
25
|
+
@field_list = FieldList.new(fields, serializer)
|
26
|
+
@include = IncludeList.new(include).parse
|
27
|
+
@serializer = SerializerFactory.create(object, serializer, self)
|
28
|
+
end
|
29
|
+
|
30
|
+
def as_json(options = nil)
|
31
|
+
root_node = ApiNode.new(serializer._root_name, serializer, include.include_hash).load
|
32
|
+
wrapper.new(root_node).as_json(options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/simple_json_api/dsl.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
|
+
require 'simple_json_api/association'
|
2
|
+
require 'simple_json_api/attribute'
|
3
|
+
require 'simple_json_api/refinements/array'
|
4
|
+
|
1
5
|
# SimpleJsonApi
|
2
6
|
module SimpleJsonApi
|
3
7
|
# Define the public API for creating serializers
|
4
8
|
module DSL
|
9
|
+
using Refinements::Array
|
10
|
+
|
5
11
|
attr_accessor :_associations
|
6
12
|
attr_accessor :_attributes
|
13
|
+
attr_accessor :_default_fields
|
14
|
+
attr_accessor :_required_associations
|
7
15
|
attr_reader :_root_name
|
8
16
|
|
9
17
|
def inherited(base)
|
@@ -12,7 +20,7 @@ module SimpleJsonApi
|
|
12
20
|
end
|
13
21
|
|
14
22
|
def serializes(name, options = {})
|
15
|
-
|
23
|
+
Serializer.register_serializer(
|
16
24
|
{ serializer: self, resource: name },
|
17
25
|
options
|
18
26
|
)
|
@@ -23,6 +31,16 @@ module SimpleJsonApi
|
|
23
31
|
register_attribute(name, options)
|
24
32
|
end
|
25
33
|
|
34
|
+
# Attributes presented if no fields specified
|
35
|
+
def default_fields(attrs)
|
36
|
+
@_default_fields = attrs
|
37
|
+
end
|
38
|
+
|
39
|
+
# Associations that are always included
|
40
|
+
def required_associations(assocs)
|
41
|
+
@_required_associations = assocs
|
42
|
+
end
|
43
|
+
|
26
44
|
def belongs_to(name, options = {})
|
27
45
|
register_association(name, :belongs_to, options)
|
28
46
|
end
|
@@ -35,7 +53,7 @@ module SimpleJsonApi
|
|
35
53
|
register_association(name, :has_one, options)
|
36
54
|
end
|
37
55
|
|
38
|
-
def
|
56
|
+
def default_attributes
|
39
57
|
@_attributes.map(&:first).join(',')
|
40
58
|
end
|
41
59
|
|
@@ -44,16 +62,28 @@ module SimpleJsonApi
|
|
44
62
|
def register_association(name, type, options)
|
45
63
|
serializer = options.fetch(:serializer, nil)
|
46
64
|
polymorphic = options.fetch(:polymorphic, false)
|
47
|
-
association =
|
65
|
+
association = Association.new(
|
48
66
|
name, type, serializer, polymorphic
|
49
67
|
)
|
50
68
|
_associations << association
|
51
|
-
|
69
|
+
define_assoc_method(association) # unless defined? association.name
|
70
|
+
end
|
71
|
+
|
72
|
+
def define_assoc_method(association)
|
73
|
+
if association.type == :has_many
|
74
|
+
define_method association.name do |includes = nil|
|
75
|
+
_object.send(association.name).includes(includes).to_a if _object.respond_to?(association.name)
|
76
|
+
end
|
77
|
+
else
|
78
|
+
define_method association.name do |includes = nil|
|
79
|
+
_object.send(association.name) if _object.respond_to?(association.name)
|
80
|
+
end
|
81
|
+
end
|
52
82
|
end
|
53
83
|
|
54
84
|
def register_attribute(name, options)
|
55
85
|
key = options.fetch(:key, nil)
|
56
|
-
attribute =
|
86
|
+
attribute = Attribute.new(name, key)
|
57
87
|
_attributes << attribute
|
58
88
|
def_delegator :_object, attribute.name
|
59
89
|
end
|
@@ -13,13 +13,10 @@ module SimpleJsonApi
|
|
13
13
|
|
14
14
|
def parse
|
15
15
|
result = {}
|
16
|
-
|
17
|
-
when Hash
|
16
|
+
if @fields.is_a? Hash
|
18
17
|
@fields.each do |resource, fields|
|
19
|
-
result[resource.to_sym] =
|
18
|
+
result[resource.to_sym] = fields.split(',').map(&:to_s)
|
20
19
|
end
|
21
|
-
when String
|
22
|
-
result[@root_serializer._root_name] = FIELD_LIST_PARSER.call(@fields)
|
23
20
|
end
|
24
21
|
result
|
25
22
|
end
|
@@ -27,7 +24,5 @@ module SimpleJsonApi
|
|
27
24
|
def fields_for(resource)
|
28
25
|
field_list.fetch(resource, nil)
|
29
26
|
end
|
30
|
-
|
31
|
-
FIELD_LIST_PARSER = ->(list) { list.split(',').map(&:to_s) }
|
32
27
|
end
|
33
28
|
end
|