simple_jsonapi 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rubocop.yml +131 -0
  4. data/CHANGELOG.md +2 -0
  5. data/Gemfile +5 -0
  6. data/Jenkinsfile +92 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +532 -0
  9. data/Rakefile +10 -0
  10. data/lib/simple_jsonapi.rb +112 -0
  11. data/lib/simple_jsonapi/definition/attribute.rb +45 -0
  12. data/lib/simple_jsonapi/definition/base.rb +50 -0
  13. data/lib/simple_jsonapi/definition/concerns/has_links_object.rb +36 -0
  14. data/lib/simple_jsonapi/definition/concerns/has_meta_object.rb +36 -0
  15. data/lib/simple_jsonapi/definition/error.rb +70 -0
  16. data/lib/simple_jsonapi/definition/error_source.rb +29 -0
  17. data/lib/simple_jsonapi/definition/link.rb +27 -0
  18. data/lib/simple_jsonapi/definition/meta.rb +27 -0
  19. data/lib/simple_jsonapi/definition/relationship.rb +60 -0
  20. data/lib/simple_jsonapi/definition/resource.rb +104 -0
  21. data/lib/simple_jsonapi/error_serializer.rb +76 -0
  22. data/lib/simple_jsonapi/errors/bad_request.rb +11 -0
  23. data/lib/simple_jsonapi/errors/exception_serializer.rb +6 -0
  24. data/lib/simple_jsonapi/errors/wrapped_error.rb +35 -0
  25. data/lib/simple_jsonapi/errors/wrapped_error_serializer.rb +35 -0
  26. data/lib/simple_jsonapi/helpers/exceptions.rb +39 -0
  27. data/lib/simple_jsonapi/helpers/serializer_inferrer.rb +136 -0
  28. data/lib/simple_jsonapi/helpers/serializer_methods.rb +36 -0
  29. data/lib/simple_jsonapi/node/attributes.rb +51 -0
  30. data/lib/simple_jsonapi/node/base.rb +91 -0
  31. data/lib/simple_jsonapi/node/data/collection.rb +25 -0
  32. data/lib/simple_jsonapi/node/data/singular.rb +26 -0
  33. data/lib/simple_jsonapi/node/document/base.rb +62 -0
  34. data/lib/simple_jsonapi/node/document/collection.rb +17 -0
  35. data/lib/simple_jsonapi/node/document/errors.rb +17 -0
  36. data/lib/simple_jsonapi/node/document/singular.rb +17 -0
  37. data/lib/simple_jsonapi/node/error.rb +55 -0
  38. data/lib/simple_jsonapi/node/error_source.rb +40 -0
  39. data/lib/simple_jsonapi/node/errors.rb +28 -0
  40. data/lib/simple_jsonapi/node/included.rb +45 -0
  41. data/lib/simple_jsonapi/node/object_links.rb +40 -0
  42. data/lib/simple_jsonapi/node/object_meta.rb +40 -0
  43. data/lib/simple_jsonapi/node/relationship.rb +79 -0
  44. data/lib/simple_jsonapi/node/relationship_data/base.rb +53 -0
  45. data/lib/simple_jsonapi/node/relationship_data/collection.rb +32 -0
  46. data/lib/simple_jsonapi/node/relationship_data/singular.rb +33 -0
  47. data/lib/simple_jsonapi/node/relationships.rb +60 -0
  48. data/lib/simple_jsonapi/node/resource/base.rb +21 -0
  49. data/lib/simple_jsonapi/node/resource/full.rb +49 -0
  50. data/lib/simple_jsonapi/node/resource/linkage.rb +25 -0
  51. data/lib/simple_jsonapi/parameters/fields_spec.rb +45 -0
  52. data/lib/simple_jsonapi/parameters/include_spec.rb +57 -0
  53. data/lib/simple_jsonapi/parameters/sort_spec.rb +107 -0
  54. data/lib/simple_jsonapi/serializer.rb +89 -0
  55. data/lib/simple_jsonapi/version.rb +3 -0
  56. data/simple_jsonapi.gemspec +29 -0
  57. data/test/errors/bad_request_test.rb +34 -0
  58. data/test/errors/error_serializer_test.rb +229 -0
  59. data/test/errors/exception_serializer_test.rb +25 -0
  60. data/test/errors/wrapped_error_serializer_test.rb +91 -0
  61. data/test/errors/wrapped_error_test.rb +44 -0
  62. data/test/parameters/fields_spec_test.rb +56 -0
  63. data/test/parameters/include_spec_test.rb +58 -0
  64. data/test/parameters/sort_spec_test.rb +65 -0
  65. data/test/resources/attributes_test.rb +109 -0
  66. data/test/resources/extras_test.rb +70 -0
  67. data/test/resources/id_and_type_test.rb +76 -0
  68. data/test/resources/inclusion_test.rb +134 -0
  69. data/test/resources/links_test.rb +63 -0
  70. data/test/resources/meta_test.rb +49 -0
  71. data/test/resources/relationships_test.rb +262 -0
  72. data/test/resources/sorting_test.rb +79 -0
  73. data/test/resources/sparse_fieldset_test.rb +160 -0
  74. data/test/root_objects_test.rb +165 -0
  75. data/test/test_helper.rb +31 -0
  76. metadata +235 -0
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << 'test'
6
+ t.libs << 'lib'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task default: :test
@@ -0,0 +1,112 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext'
3
+
4
+ require 'simple_jsonapi/helpers/exceptions'
5
+ require 'simple_jsonapi/helpers/serializer_inferrer'
6
+ require 'simple_jsonapi/helpers/serializer_methods'
7
+
8
+ require 'simple_jsonapi/parameters/fields_spec'
9
+ require 'simple_jsonapi/parameters/include_spec'
10
+ require 'simple_jsonapi/parameters/sort_spec'
11
+
12
+ require 'simple_jsonapi/definition/base'
13
+ require 'simple_jsonapi/definition/attribute'
14
+ require 'simple_jsonapi/definition/error'
15
+ require 'simple_jsonapi/definition/error_source'
16
+ require 'simple_jsonapi/definition/link'
17
+ require 'simple_jsonapi/definition/meta'
18
+ require 'simple_jsonapi/definition/relationship'
19
+ require 'simple_jsonapi/definition/resource'
20
+
21
+ require 'simple_jsonapi/node/base'
22
+ require 'simple_jsonapi/node/attributes'
23
+ require 'simple_jsonapi/node/data/collection'
24
+ require 'simple_jsonapi/node/data/singular'
25
+ require 'simple_jsonapi/node/document/base'
26
+ require 'simple_jsonapi/node/document/collection'
27
+ require 'simple_jsonapi/node/document/singular'
28
+ require 'simple_jsonapi/node/document/errors'
29
+ require 'simple_jsonapi/node/error'
30
+ require 'simple_jsonapi/node/error_source'
31
+ require 'simple_jsonapi/node/errors'
32
+ require 'simple_jsonapi/node/included'
33
+ require 'simple_jsonapi/node/object_links'
34
+ require 'simple_jsonapi/node/object_meta'
35
+ require 'simple_jsonapi/node/relationship'
36
+ require 'simple_jsonapi/node/relationship_data/base'
37
+ require 'simple_jsonapi/node/relationship_data/collection'
38
+ require 'simple_jsonapi/node/relationship_data/singular'
39
+ require 'simple_jsonapi/node/relationships'
40
+ require 'simple_jsonapi/node/resource/base'
41
+ require 'simple_jsonapi/node/resource/full'
42
+ require 'simple_jsonapi/node/resource/linkage'
43
+
44
+ require 'simple_jsonapi/serializer'
45
+ require 'simple_jsonapi/error_serializer'
46
+
47
+ require 'simple_jsonapi/errors/bad_request'
48
+ require 'simple_jsonapi/errors/exception_serializer'
49
+ require 'simple_jsonapi/errors/wrapped_error'
50
+ require 'simple_jsonapi/errors/wrapped_error_serializer'
51
+
52
+ module SimpleJsonapi
53
+ MIME_TYPE = 'application/vnd.api+json'.freeze
54
+
55
+ mattr_accessor :serializer_inferrer, :error_serializer_inferrer
56
+
57
+ self.serializer_inferrer = SerializerInferrer.new
58
+ self.error_serializer_inferrer = SerializerInferrer.new(namespace: SimpleJsonapi::Errors.name)
59
+
60
+ def self.render_resource(resource, options = {})
61
+ document_options = normalize_render_options(
62
+ options,
63
+ resource: resource,
64
+ serializer: serializer_inferrer,
65
+ )
66
+
67
+ Node::Document::Singular.new(document_options).as_jsonapi
68
+ end
69
+
70
+ def self.render_resources(resources, options = {})
71
+ document_options = normalize_render_options(
72
+ options,
73
+ resources: resources,
74
+ serializer: serializer_inferrer,
75
+ )
76
+
77
+ Node::Document::Collection.new(document_options).as_jsonapi
78
+ end
79
+
80
+ def self.render_errors(errors, options = {})
81
+ document_options = normalize_render_options(
82
+ options,
83
+ errors: errors,
84
+ serializer: error_serializer_inferrer,
85
+ )
86
+
87
+ Node::Document::Errors.new(document_options).as_jsonapi
88
+ end
89
+
90
+ def self.normalize_render_options(options, defaults)
91
+ defaults.merge(options.symbolize_keys).transform_keys do |key|
92
+ key == :serializer ? :serializer_inferrer : key
93
+ end
94
+ end
95
+
96
+ private_class_method :normalize_render_options
97
+ end
98
+
99
+ module SimpleJsonapi::Definition
100
+ end
101
+
102
+ module SimpleJsonapi::Definition::Concerns
103
+ end
104
+
105
+ module SimpleJsonapi::Errors
106
+ end
107
+
108
+ module SimpleJsonapi::Node
109
+ end
110
+
111
+ module SimpleJsonapi::Parameters
112
+ end
@@ -0,0 +1,45 @@
1
+ # Represents a single attribute in a resource's +attributes+ collection
2
+ # @!attribute [r] name
3
+ # @return [Symbol]
4
+ # @!attribute [r] data_type
5
+ # @return [Symbol]
6
+ # @!attribute [r] array
7
+ # @return [Boolean]
8
+ # @!attribute [r] description
9
+ # @return [String]
10
+ class SimpleJsonapi::Definition::Attribute < SimpleJsonapi::Definition::Base
11
+ attr_reader :name, :data_type, :array, :description
12
+ # @visibility private
13
+ attr_reader :value_proc
14
+
15
+ # @overload initialize(name, type: nil, description: nil, **options, &block)
16
+ # @overload initialize(name, value, type: nil, description: nil, **options)
17
+ # @param type [Symbol] data type
18
+ # @param description [String]
19
+ # @yieldparam resource [Object]
20
+ # @yieldreturn [Object]
21
+ # @option (see Definition::Base#initialize)
22
+ def initialize(name, *args, type: nil, array: false, description: nil, **options, &block)
23
+ raise ArgumentError, "A name is required" if name.blank?
24
+
25
+ super
26
+ @name = name.to_sym
27
+ @data_type = type&.to_sym
28
+ @array = array
29
+ @description = description.to_s.presence
30
+
31
+ if args.none? && !block_given?
32
+ @value_proc = wrap_in_proc { |resource| resource.public_send(name) }
33
+ else
34
+ @value_proc = wrap_in_proc(*args, &block)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def initialize_dup(new_def)
41
+ super
42
+ # name is a symbol, can't be duped
43
+ new_def.instance_variable_set(:@value_proc, @value_proc.dup)
44
+ end
45
+ end
@@ -0,0 +1,50 @@
1
+ require 'simple_jsonapi/definition/concerns/has_links_object'
2
+ require 'simple_jsonapi/definition/concerns/has_meta_object'
3
+
4
+ # Defines how a portion of a rendered JSONAPI document is generated from a
5
+ # resource or error object. See {file:README.md} for more details.
6
+ #
7
+ # @abstract
8
+ class SimpleJsonapi::Definition::Base
9
+ # @visibility private
10
+ # attr_reader :options, :if_predicate, :unless_predicate
11
+ attr_reader :if_predicate, :unless_predicate
12
+
13
+ # @param args See subclass documentation for the arguments they require.
14
+ # @option options [Proc<Boolean>] if
15
+ # @option options [Proc<Boolean>] unless
16
+ def initialize(*_args, **options, &_block)
17
+ @if_predicate = @unless_predicate = nil
18
+ @if_predicate = wrap_in_proc(options[:if]) if options.key?(:if)
19
+ @unless_predicate = wrap_in_proc(options[:unless]) if options.key?(:unless)
20
+ end
21
+
22
+ private def initialize_dup(new_def)
23
+ super
24
+
25
+ new_def.instance_variable_set(:@if_predicate, @if_predicate.dup) unless @if_predicate.nil?
26
+ new_def.instance_variable_set(:@unless_predicate, @unless_predicate.dup) unless @unless_predicate.nil?
27
+ end
28
+
29
+ private
30
+
31
+ def wrap_in_proc(*args, &block)
32
+ if args.empty? && !block_given?
33
+ raise ArgumentError, "Either a value or a block is required"
34
+ elsif args.size == 1 && block_given?
35
+ raise ArgumentError, "A value and a block cannot both be present"
36
+ elsif args.size > 1
37
+ raise ArgumentError, "Too many arguments provided (#{args.size})"
38
+ end
39
+
40
+ callable_or_value = args[0]
41
+
42
+ if block_given?
43
+ block
44
+ elsif callable_or_value.is_a?(Proc)
45
+ callable_or_value
46
+ else
47
+ proc { |*| callable_or_value }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,36 @@
1
+ module SimpleJsonapi
2
+ module Definition
3
+ module Concerns
4
+ # Adds a {#link} method and {#link_definitions} collection to a
5
+ # definition.
6
+ #
7
+ # @!attribute [r] link_definitions
8
+ # @return [Hash{Symbol => String,Object}]
9
+ module HasLinksObject
10
+ # @visibility private
11
+ def self.included(base)
12
+ base.send :attr_accessor, :link_definitions
13
+ end
14
+
15
+ def initialize(*args, &block)
16
+ super
17
+ @link_definitions = {}
18
+ end
19
+
20
+ private def initialize_dup(new_def)
21
+ super
22
+ new_def.link_definitions = link_definitions.deep_dup
23
+ end
24
+
25
+ # @overload link(name, options = {}, &block)
26
+ # @overload link(name, value, options = {})
27
+ # @yieldparam object [Object]
28
+ # @yieldreturn [String,Hash]
29
+ # @return [void]
30
+ def link(name, *args, **options, &block)
31
+ link_definitions[name.to_sym] = Link.new(name, *args, options, &block)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ module SimpleJsonapi
2
+ module Definition
3
+ module Concerns
4
+ # Adds a {#meta} method and {#meta_definitions} collection to a
5
+ # definition.
6
+ #
7
+ # @!attribute [r] meta_definitions
8
+ # @return [Hash{Symbol => String,Object}]
9
+ module HasMetaObject
10
+ # @visibility private
11
+ def self.included(base)
12
+ base.send :attr_accessor, :meta_definitions
13
+ end
14
+
15
+ def initialize(*args, &block)
16
+ super
17
+ @meta_definitions = {}
18
+ end
19
+
20
+ private def initialize_dup(new_def)
21
+ super
22
+ new_def.meta_definitions = meta_definitions.deep_dup
23
+ end
24
+
25
+ # @overload meta(name, options = {}, &block)
26
+ # @overload meta(name, value, options = {})
27
+ # @yieldparam object [Object]
28
+ # @yieldreturn [Object]
29
+ # @return [void]
30
+ def meta(name, *args, **options, &block)
31
+ meta_definitions[name.to_sym] = Meta.new(name, *args, options, &block)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,70 @@
1
+ # @!attribute [r] member_definitions
2
+ # @return [Hash{Symbol => Attribute}]
3
+ # @!attribute [r] source_definition
4
+ # @return [ErrorSource]
5
+ class SimpleJsonapi::Definition::Error < SimpleJsonapi::Definition::Base
6
+ include SimpleJsonapi::Definition::Concerns::HasLinksObject
7
+ include SimpleJsonapi::Definition::Concerns::HasMetaObject
8
+
9
+ attr_reader :member_definitions, :source_definition
10
+
11
+ def initialize
12
+ super
13
+ @member_definitions = {}
14
+ @source_definition = SimpleJsonapi::Definition::ErrorSource.new
15
+ end
16
+
17
+ private def initialize_dup(new_def)
18
+ super
19
+ new_def.instance_variable_set(:@source_definition, @source_definition.dup) unless @source_definition.nil?
20
+ new_def.instance_variable_set(:@member_definitions, @member_definitions.dup) unless @member_definitions.nil?
21
+ end
22
+
23
+ # @overload id(options = {}, &block)
24
+ # @overload id(value, options = {})
25
+ # @return [void]
26
+ def id(*args, **options, &block)
27
+ member_definitions[:id] = SimpleJsonapi::Definition::Attribute.new(:id, *args, **options, &block)
28
+ end
29
+
30
+ # @overload status(options = {}, &block)
31
+ # @overload status(value, options = {})
32
+ # @return [void]
33
+ def status(*args, **options, &block)
34
+ member_definitions[:status] = SimpleJsonapi::Definition::Attribute.new(:status, *args, **options, &block)
35
+ end
36
+
37
+ # @overload code(options = {}, &block)
38
+ # @overload code(value, options = {})
39
+ # @return [void]
40
+ def code(*args, **options, &block)
41
+ member_definitions[:code] = SimpleJsonapi::Definition::Attribute.new(:code, *args, **options, &block)
42
+ end
43
+
44
+ # @overload title(options = {}, &block)
45
+ # @overload title(value, options = {})
46
+ # @return [void]
47
+ def title(*args, **options, &block)
48
+ member_definitions[:title] = SimpleJsonapi::Definition::Attribute.new(:title, *args, **options, &block)
49
+ end
50
+
51
+ # @overload detail(options = {}, &block)
52
+ # @overload detail(value, options = {})
53
+ # @return [void]
54
+ def detail(*args, **options, &block)
55
+ member_definitions[:detail] = SimpleJsonapi::Definition::Attribute.new(:detail, *args, **options, &block)
56
+ end
57
+
58
+ # @overload about_link(options = {}, &block)
59
+ # @overload about_link(value, options = {})
60
+ # @return [void]
61
+ def about_link(*args, **options, &block)
62
+ link(:about, *args, options, &block)
63
+ end
64
+
65
+ # @see ErrorSource#initialize
66
+ # @return [void]
67
+ def source(&block)
68
+ @source_definition = SimpleJsonapi::Definition::ErrorSource.new(&block)
69
+ end
70
+ end
@@ -0,0 +1,29 @@
1
+ class SimpleJsonapi::Definition::ErrorSource < SimpleJsonapi::Definition::Base
2
+ # @visibility private
3
+ attr_reader :member_definitions
4
+
5
+ def initialize(&block)
6
+ super
7
+ @member_definitions = {}
8
+ instance_eval(&block) if block_given?
9
+ end
10
+
11
+ private def initialize_dup(new_def)
12
+ super
13
+ new_def.instance_variable_set(:@member_definitions, @member_definitions.dup) unless @member_definitions.nil?
14
+ end
15
+
16
+ # @overload pointer(options = {}, &block)
17
+ # @overload pointer(value, options = {})
18
+ # @return [void]
19
+ def pointer(*args, **options, &block)
20
+ member_definitions[:pointer] = SimpleJsonapi::Definition::Attribute.new(:pointer, *args, **options, &block)
21
+ end
22
+
23
+ # @overload parameter(options = {}, &block)
24
+ # @overload parameter(value, options = {})
25
+ # @return [void]
26
+ def parameter(*args, **options, &block)
27
+ member_definitions[:parameter] = SimpleJsonapi::Definition::Attribute.new(:parameter, *args, **options, &block)
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ # Represents a single member in a +links+ object
2
+ # @!attribute [r] name
3
+ # @return [Symbol]
4
+ class SimpleJsonapi::Definition::Link < SimpleJsonapi::Definition::Base
5
+ attr_reader :name
6
+ # @visibility private
7
+ attr_reader :value_proc
8
+
9
+ # @overload initialize(name, options = {}, &block)
10
+ # @overload initialize(name, value, options = {})
11
+ # @yieldparam object [Object] The resource or error.
12
+ # @yieldreturn [String,Hash] The link or link object.
13
+ # @option (see Definition::Base#initialize)
14
+ def initialize(name, *args, **options, &block)
15
+ raise ArgumentError, "A name is required" if name.blank?
16
+
17
+ super
18
+ @name = name
19
+ @value_proc = wrap_in_proc(*args, &block)
20
+ end
21
+
22
+ private def initialize_dup(new_def)
23
+ super
24
+ # name is a symbol, can't be duped
25
+ new_def.instance_variable_set(:@value_proc, @value_proc.dup)
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # Represents a single member in a +meta+ object
2
+ # @!attribute [r] name
3
+ # @return [Symbol]
4
+ class SimpleJsonapi::Definition::Meta < SimpleJsonapi::Definition::Base
5
+ attr_reader :name
6
+ # @visibility private
7
+ attr_reader :value_proc
8
+
9
+ # @overload initialize(name, options = {}, &block)
10
+ # @overload initialize(name, value, options = {})
11
+ # @yieldparam object [Object] The resource or error.
12
+ # @yieldreturn [String,Hash] The metadata value.
13
+ # @option (see Definition::Base#initialize)
14
+ def initialize(name, *args, **options, &block)
15
+ raise ArgumentError, "A name is required" if name.blank?
16
+
17
+ super
18
+ @name = name
19
+ @value_proc = wrap_in_proc(*args, &block)
20
+ end
21
+
22
+ private def initialize_dup(new_def)
23
+ super
24
+ # name is a symbol, can't be duped
25
+ new_def.instance_variable_set(:@value_proc, @value_proc.dup)
26
+ end
27
+ end