simple_jsonapi 1.0.0

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 (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