media_types 2.3.0 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/debian.yml +43 -43
  3. data/.github/workflows/publish-bookworm.yml +34 -33
  4. data/.github/workflows/publish-sid.yml +34 -33
  5. data/.github/workflows/ruby.yml +22 -22
  6. data/.gitignore +20 -20
  7. data/.rubocop.yml +29 -29
  8. data/CHANGELOG.md +183 -175
  9. data/Gemfile +6 -6
  10. data/Gemfile.lock +43 -43
  11. data/LICENSE +21 -21
  12. data/README.md +666 -666
  13. data/Rakefile +12 -12
  14. data/bin/console +15 -15
  15. data/bin/setup +8 -8
  16. data/lib/media_types/constructable.rb +161 -161
  17. data/lib/media_types/dsl/errors.rb +18 -18
  18. data/lib/media_types/dsl.rb +172 -172
  19. data/lib/media_types/errors.rb +25 -25
  20. data/lib/media_types/formatter.rb +56 -56
  21. data/lib/media_types/hash.rb +21 -21
  22. data/lib/media_types/object.rb +35 -35
  23. data/lib/media_types/scheme/allow_nil.rb +30 -30
  24. data/lib/media_types/scheme/any_of.rb +41 -41
  25. data/lib/media_types/scheme/attribute.rb +46 -46
  26. data/lib/media_types/scheme/enumeration_context.rb +18 -18
  27. data/lib/media_types/scheme/enumeration_of_type.rb +80 -80
  28. data/lib/media_types/scheme/errors.rb +87 -87
  29. data/lib/media_types/scheme/links.rb +54 -54
  30. data/lib/media_types/scheme/missing_validation.rb +41 -41
  31. data/lib/media_types/scheme/not_strict.rb +15 -15
  32. data/lib/media_types/scheme/output_empty_guard.rb +45 -45
  33. data/lib/media_types/scheme/output_iterator_with_predicate.rb +66 -66
  34. data/lib/media_types/scheme/output_type_guard.rb +39 -39
  35. data/lib/media_types/scheme/rules.rb +186 -186
  36. data/lib/media_types/scheme/rules_exhausted_guard.rb +75 -73
  37. data/lib/media_types/scheme/validation_options.rb +44 -44
  38. data/lib/media_types/scheme.rb +535 -535
  39. data/lib/media_types/testing/assertions.rb +20 -20
  40. data/lib/media_types/validations.rb +118 -118
  41. data/lib/media_types/version.rb +5 -5
  42. data/lib/media_types/views.rb +12 -12
  43. data/lib/media_types.rb +73 -73
  44. data/media_types.gemspec +33 -33
  45. metadata +6 -6
@@ -1,172 +1,172 @@
1
- # frozen_string_literal: true
2
-
3
- require 'set'
4
-
5
- require 'media_types/constructable'
6
- require 'media_types/validations'
7
-
8
- require 'media_types/dsl/errors'
9
-
10
- module MediaTypes
11
- module Dsl
12
- def self.included(base)
13
- base.extend ClassMethods
14
- base.class_eval do
15
- class << self
16
- attr_accessor :media_type_name_for, :media_type_combinations, :media_type_validations, :symbol_keys
17
-
18
- private
19
-
20
- attr_accessor :media_type_constructable, :symbol_base, :media_type_registrar
21
- end
22
- base.media_type_combinations = Set.new
23
- end
24
- end
25
-
26
- module ClassMethods
27
- def to_constructable
28
- raise UninitializedConstructable if media_type_constructable.nil?
29
-
30
- media_type_constructable.dup.tap do |constructable|
31
- constructable.__setobj__(self)
32
- end
33
- end
34
-
35
- def symbol_keys?
36
- if symbol_keys.nil?
37
- MediaTypes.get_key_expectation(self)
38
- else
39
- symbol_keys
40
- end
41
- end
42
-
43
- def string_keys?
44
- !symbol_keys?
45
- end
46
-
47
- def valid?(output, **opts)
48
- to_constructable.valid?(output, **opts)
49
- end
50
-
51
- def valid_unsafe?(output, media_type = to_constructable, **opts)
52
- opts[:expected_key_type] = string_keys? ? String : Symbol
53
- validations.find(media_type).valid?(output, backtrace: ['.'], **opts)
54
- end
55
-
56
- def validate!(output, **opts)
57
- assert_sane!
58
- to_constructable.validate!(output, **opts)
59
- end
60
-
61
- def validate_unsafe!(output, media_type = to_constructable, **opts)
62
- opts[:expected_key_type] = string_keys? ? String : Symbol
63
- validations.find(media_type).validate(output, backtrace: ['.'], **opts)
64
- end
65
-
66
- def validatable?(media_type = to_constructable)
67
- return false unless validations
68
-
69
- resolved = validations.find(media_type, -> { nil })
70
-
71
- !resolved.nil?
72
- end
73
-
74
- def register
75
- registrations.to_a.map do |registerable|
76
- MediaTypes.register(registerable)
77
- registerable
78
- end
79
- end
80
-
81
- def view(v)
82
- to_constructable.view(v)
83
- end
84
-
85
- def version(v)
86
- to_constructable.version(v)
87
- end
88
-
89
- def identifier_format
90
- self.media_type_name_for = proc do |type:, view:, version:, suffix:|
91
- yield(type: type, view: view, version: version, suffix: suffix)
92
- end
93
- end
94
-
95
- def identifier
96
- to_constructable.to_s
97
- end
98
-
99
- def available_validations
100
- media_type_combinations.map do |a|
101
- _, view, version = a
102
- view(view).version(version)
103
- end
104
- end
105
-
106
- def schema_for(constructable)
107
- validations.find(constructable)
108
- end
109
-
110
- def assert_sane!
111
- return if media_type_validations.scheme.asserted_sane?
112
-
113
- media_type_validations.run_fixture_validations(symbol_keys?)
114
- end
115
-
116
- private
117
-
118
- def use_name(name)
119
- if media_type_name_for.nil?
120
- self.media_type_name_for = proc do |type:, view:, version:, suffix:|
121
- resolved_org = nil
122
- if defined?(organisation)
123
- resolved_org = organisation
124
- else
125
- resolved_org = MediaTypes.get_organisation(self)
126
-
127
- if resolved_org.nil?
128
- raise OrganisationNotSetError,
129
- format('Implement the class method "organisation" in %<klass>s or specify a global organisation using MediaTypes::set_organisation', klass: self)
130
- end
131
- end
132
- raise ArgumentError, 'Unable to create a name for a schema with a nil name.' if type.nil?
133
- raise ArgumentError, 'Unable to create a name for a schema with a nil organisation.' if resolved_org.nil?
134
-
135
- result = "application/vnd.#{resolved_org}.#{type}"
136
- result += ".v#{version}" unless version.nil?
137
- result += ".#{view}" unless view.nil?
138
- result += "+#{suffix}" unless suffix.nil?
139
- result
140
- end
141
- end
142
- self.media_type_constructable = Constructable.new(self, type: name)
143
- end
144
-
145
- def expect_string_keys
146
- raise KeyTypeExpectationError, 'Key expectation already set' unless symbol_keys.nil?
147
-
148
- self.symbol_keys = false
149
- end
150
-
151
- def expect_symbol_keys
152
- raise KeyTypeExpectationError, 'Key expectation already set' unless symbol_keys.nil?
153
-
154
- self.symbol_keys = true
155
- end
156
-
157
- def validations(&block)
158
- return lookup_validations unless block_given?
159
-
160
- self.media_type_validations = Validations.new(to_constructable, &block)
161
-
162
- self
163
- end
164
-
165
- def lookup_validations
166
- raise MissingValidationError, "No validations defined for #{name}" if media_type_validations.nil?
167
-
168
- media_type_validations
169
- end
170
- end
171
- end
172
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ require 'media_types/constructable'
6
+ require 'media_types/validations'
7
+
8
+ require 'media_types/dsl/errors'
9
+
10
+ module MediaTypes
11
+ module Dsl
12
+ def self.included(base)
13
+ base.extend ClassMethods
14
+ base.class_eval do
15
+ class << self
16
+ attr_accessor :media_type_name_for, :media_type_combinations, :media_type_validations, :symbol_keys
17
+
18
+ private
19
+
20
+ attr_accessor :media_type_constructable, :symbol_base, :media_type_registrar
21
+ end
22
+ base.media_type_combinations = Set.new
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ def to_constructable
28
+ raise UninitializedConstructable if media_type_constructable.nil?
29
+
30
+ media_type_constructable.dup.tap do |constructable|
31
+ constructable.__setobj__(self)
32
+ end
33
+ end
34
+
35
+ def symbol_keys?
36
+ if symbol_keys.nil?
37
+ MediaTypes.get_key_expectation(self)
38
+ else
39
+ symbol_keys
40
+ end
41
+ end
42
+
43
+ def string_keys?
44
+ !symbol_keys?
45
+ end
46
+
47
+ def valid?(output, **opts)
48
+ to_constructable.valid?(output, **opts)
49
+ end
50
+
51
+ def valid_unsafe?(output, media_type = to_constructable, **opts)
52
+ opts[:expected_key_type] = string_keys? ? String : Symbol
53
+ validations.find(media_type).valid?(output, backtrace: ['.'], **opts)
54
+ end
55
+
56
+ def validate!(output, **opts)
57
+ assert_sane!
58
+ to_constructable.validate!(output, **opts)
59
+ end
60
+
61
+ def validate_unsafe!(output, media_type = to_constructable, **opts)
62
+ opts[:expected_key_type] = string_keys? ? String : Symbol
63
+ validations.find(media_type).validate(output, backtrace: ['.'], **opts)
64
+ end
65
+
66
+ def validatable?(media_type = to_constructable)
67
+ return false unless validations
68
+
69
+ resolved = validations.find(media_type, -> { nil })
70
+
71
+ !resolved.nil?
72
+ end
73
+
74
+ def register
75
+ registrations.to_a.map do |registerable|
76
+ MediaTypes.register(registerable)
77
+ registerable
78
+ end
79
+ end
80
+
81
+ def view(v)
82
+ to_constructable.view(v)
83
+ end
84
+
85
+ def version(v)
86
+ to_constructable.version(v)
87
+ end
88
+
89
+ def identifier_format
90
+ self.media_type_name_for = proc do |type:, view:, version:, suffix:|
91
+ yield(type: type, view: view, version: version, suffix: suffix)
92
+ end
93
+ end
94
+
95
+ def identifier
96
+ to_constructable.to_s
97
+ end
98
+
99
+ def available_validations
100
+ media_type_combinations.map do |a|
101
+ _, view, version = a
102
+ view(view).version(version)
103
+ end
104
+ end
105
+
106
+ def schema_for(constructable)
107
+ validations.find(constructable)
108
+ end
109
+
110
+ def assert_sane!
111
+ return if media_type_validations.scheme.asserted_sane?
112
+
113
+ media_type_validations.run_fixture_validations(symbol_keys?)
114
+ end
115
+
116
+ private
117
+
118
+ def use_name(name)
119
+ if media_type_name_for.nil?
120
+ self.media_type_name_for = proc do |type:, view:, version:, suffix:|
121
+ resolved_org = nil
122
+ if defined?(organisation)
123
+ resolved_org = organisation
124
+ else
125
+ resolved_org = MediaTypes.get_organisation(self)
126
+
127
+ if resolved_org.nil?
128
+ raise OrganisationNotSetError,
129
+ format('Implement the class method "organisation" in %<klass>s or specify a global organisation using MediaTypes::set_organisation', klass: self)
130
+ end
131
+ end
132
+ raise ArgumentError, 'Unable to create a name for a schema with a nil name.' if type.nil?
133
+ raise ArgumentError, 'Unable to create a name for a schema with a nil organisation.' if resolved_org.nil?
134
+
135
+ result = "application/vnd.#{resolved_org}.#{type}"
136
+ result += ".v#{version}" unless version.nil?
137
+ result += ".#{view}" unless view.nil?
138
+ result += "+#{suffix}" unless suffix.nil?
139
+ result
140
+ end
141
+ end
142
+ self.media_type_constructable = Constructable.new(self, type: name)
143
+ end
144
+
145
+ def expect_string_keys
146
+ raise KeyTypeExpectationError, 'Key expectation already set' unless symbol_keys.nil?
147
+
148
+ self.symbol_keys = false
149
+ end
150
+
151
+ def expect_symbol_keys
152
+ raise KeyTypeExpectationError, 'Key expectation already set' unless symbol_keys.nil?
153
+
154
+ self.symbol_keys = true
155
+ end
156
+
157
+ def validations(&block)
158
+ return lookup_validations unless block_given?
159
+
160
+ self.media_type_validations = Validations.new(to_constructable, &block)
161
+
162
+ self
163
+ end
164
+
165
+ def lookup_validations
166
+ raise MissingValidationError, "No validations defined for #{name}" if media_type_validations.nil?
167
+
168
+ media_type_validations
169
+ end
170
+ end
171
+ end
172
+ end
@@ -1,25 +1,25 @@
1
- # frozen_string_literal: true
2
-
3
- module MediaTypes
4
- module Errors
5
- # Raised when trying to set a module key expectation twice
6
- class KeyExpectationSetError < StandardError
7
- def initialize(mod:)
8
- super(format('%<mod>s already has a key expectation set', mod: mod.name))
9
- end
10
- end
11
-
12
- # Raised when trying to set a module key expectation while default expectation already used
13
- class KeyExpectationUsedError < StandardError
14
- def initialize(mod:)
15
- super(format('Unable to change key type expectation for %<mod>s since its current expectation is already used', mod: mod.name))
16
- end
17
- end
18
-
19
- class CollectionDefinitionNotFound < StandardError
20
- def initialize(current, target)
21
- super(format('Unable to use %<target>s as a collection inside %<current>s, no such schema has been defined.', current: current, target: target))
22
- end
23
- end
24
- end
25
- end
1
+ # frozen_string_literal: true
2
+
3
+ module MediaTypes
4
+ module Errors
5
+ # Raised when trying to set a module key expectation twice
6
+ class KeyExpectationSetError < StandardError
7
+ def initialize(mod:)
8
+ super(format('%<mod>s already has a key expectation set', mod: mod.name))
9
+ end
10
+ end
11
+
12
+ # Raised when trying to set a module key expectation while default expectation already used
13
+ class KeyExpectationUsedError < StandardError
14
+ def initialize(mod:)
15
+ super(format('Unable to change key type expectation for %<mod>s since its current expectation is already used', mod: mod.name))
16
+ end
17
+ end
18
+
19
+ class CollectionDefinitionNotFound < StandardError
20
+ def initialize(current, target)
21
+ super(format('Unable to use %<target>s as a collection inside %<current>s, no such schema has been defined.', current: current, target: target))
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,56 +1,56 @@
1
- # frozen_string_literal: true
2
-
3
- require 'media_types/object'
4
-
5
- module MediaTypes
6
- class Formatter < DelegateClass(Hash)
7
-
8
- class << self
9
- def call(*args, **options)
10
- new(*args, **options).call
11
- end
12
- end
13
-
14
- def call
15
- filtered_arguments = arguments
16
- return template if MediaTypes::Object.new(filtered_arguments).empty?
17
-
18
- format(rework_template(filtered_arguments), filtered_arguments)
19
- end
20
-
21
- private
22
-
23
- def template
24
- fetch(:format)
25
- end
26
-
27
- def rework_template(filtered_arguments)
28
- filtered_arguments.reduce(template) do |reworked, (key, value)|
29
- next reworked if MediaTypes::Object.new(value).present?
30
-
31
- reworked.gsub(/[.+](%<[A-z]+>)/) do |match|
32
- partial_format = "%<#{key}>"
33
- match.include?(partial_format) ? partial_format : match
34
- end
35
- end
36
- end
37
-
38
- def format_view(view)
39
- MediaTypes::Object.new(view).present? && ".#{view}" || ''
40
- end
41
-
42
- def arguments
43
- # noinspection RubyBlockToMethodReference
44
- {
45
- version: self[:version],
46
- suffix: self[:suffix],
47
- type: self[:type],
48
- view: self[:view]
49
- }.select { |argument,| argument_present?(argument) }
50
- end
51
-
52
- def argument_present?(argument)
53
- template.include?("%<#{argument}>")
54
- end
55
- end
56
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'media_types/object'
4
+
5
+ module MediaTypes
6
+ class Formatter < DelegateClass(Hash)
7
+
8
+ class << self
9
+ def call(*args, **options)
10
+ new(*args, **options).call
11
+ end
12
+ end
13
+
14
+ def call
15
+ filtered_arguments = arguments
16
+ return template if MediaTypes::Object.new(filtered_arguments).empty?
17
+
18
+ format(rework_template(filtered_arguments), filtered_arguments)
19
+ end
20
+
21
+ private
22
+
23
+ def template
24
+ fetch(:format)
25
+ end
26
+
27
+ def rework_template(filtered_arguments)
28
+ filtered_arguments.reduce(template) do |reworked, (key, value)|
29
+ next reworked if MediaTypes::Object.new(value).present?
30
+
31
+ reworked.gsub(/[.+](%<[A-z]+>)/) do |match|
32
+ partial_format = "%<#{key}>"
33
+ match.include?(partial_format) ? partial_format : match
34
+ end
35
+ end
36
+ end
37
+
38
+ def format_view(view)
39
+ MediaTypes::Object.new(view).present? && ".#{view}" || ''
40
+ end
41
+
42
+ def arguments
43
+ # noinspection RubyBlockToMethodReference
44
+ {
45
+ version: self[:version],
46
+ suffix: self[:suffix],
47
+ type: self[:type],
48
+ view: self[:view]
49
+ }.select { |argument,| argument_present?(argument) }
50
+ end
51
+
52
+ def argument_present?(argument)
53
+ template.include?("%<#{argument}>")
54
+ end
55
+ end
56
+ end
@@ -1,21 +1,21 @@
1
- module MediaTypes
2
- class Hash < SimpleDelegator
3
- def class
4
- __getobj__.class
5
- end
6
-
7
- def ===(other)
8
- __getobj__ === other # rubocop:disable Style/CaseEquality
9
- end
10
-
11
- def slice(*keep_keys)
12
- if __getobj__.respond_to?(:slice)
13
- return __getobj__.slice(*keep_keys)
14
- end
15
-
16
- h = {}
17
- keep_keys.each { |key| h[key] = fetch(key) if key?(key) }
18
- h
19
- end
20
- end
21
- end
1
+ module MediaTypes
2
+ class Hash < SimpleDelegator
3
+ def class
4
+ __getobj__.class
5
+ end
6
+
7
+ def ===(other)
8
+ __getobj__ === other # rubocop:disable Style/CaseEquality
9
+ end
10
+
11
+ def slice(*keep_keys)
12
+ if __getobj__.respond_to?(:slice)
13
+ return __getobj__.slice(*keep_keys)
14
+ end
15
+
16
+ h = {}
17
+ keep_keys.each { |key| h[key] = fetch(key) if key?(key) }
18
+ h
19
+ end
20
+ end
21
+ end
@@ -1,35 +1,35 @@
1
- # frozen_string_literal: true
2
-
3
- module MediaTypes
4
- class Object < SimpleDelegator
5
- def class
6
- __getobj__.class
7
- end
8
-
9
- def ===(other)
10
- __getobj__ === other # rubocop:disable Style/CaseEquality
11
- end
12
-
13
- def blank?
14
- if __getobj__.respond_to?(:blank?)
15
- return __getobj__.blank?
16
- end
17
-
18
- if __getobj__.respond_to?(:empty?)
19
- return __getobj__.empty?
20
- end
21
-
22
- if __getobj__.respond_to?(:length)
23
- return __getobj__.length.zero?
24
- end
25
-
26
- !__getobj__
27
- end
28
-
29
- alias empty? blank?
30
-
31
- def present?
32
- !blank?
33
- end
34
- end
35
- end
1
+ # frozen_string_literal: true
2
+
3
+ module MediaTypes
4
+ class Object < SimpleDelegator
5
+ def class
6
+ __getobj__.class
7
+ end
8
+
9
+ def ===(other)
10
+ __getobj__ === other # rubocop:disable Style/CaseEquality
11
+ end
12
+
13
+ def blank?
14
+ if __getobj__.respond_to?(:blank?)
15
+ return __getobj__.blank?
16
+ end
17
+
18
+ if __getobj__.respond_to?(:empty?)
19
+ return __getobj__.empty?
20
+ end
21
+
22
+ if __getobj__.respond_to?(:length)
23
+ return __getobj__.length.zero?
24
+ end
25
+
26
+ !__getobj__
27
+ end
28
+
29
+ alias empty? blank?
30
+
31
+ def present?
32
+ !blank?
33
+ end
34
+ end
35
+ end
@@ -1,30 +1,30 @@
1
- # frozen_string_literal: true
2
-
3
- require 'delegate'
4
- require 'media_types/scheme/any_of'
5
-
6
- module MediaTypes
7
- class Scheme
8
- class << self
9
- # noinspection RubyClassMethodNamingConvention
10
- ##
11
- # Allows the wrapped +klazz+ to be nil
12
- #
13
- # @param [Class] klazz the class that +it+ must be the if +it+ is not NilClass
14
- # @return [CaseEqualityWithList]
15
- def AllowNil(klazz) # rubocop:disable Naming/MethodName
16
- AnyOf(NilClass, klazz)
17
- end
18
- end
19
-
20
- # noinspection RubyInstanceMethodNamingConvention
21
- ##
22
- # Allows the wrapped +klazz+ to be nil
23
- #
24
- # @param [Class] klazz the class that +it+ must be the if +it+ is not NilClass
25
- # @return [CaseEqualityWithList]
26
- def AllowNil(klazz) # rubocop:disable Naming/MethodName
27
- self.class.AllowNil(klazz)
28
- end
29
- end
30
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+ require 'media_types/scheme/any_of'
5
+
6
+ module MediaTypes
7
+ class Scheme
8
+ class << self
9
+ # noinspection RubyClassMethodNamingConvention
10
+ ##
11
+ # Allows the wrapped +klazz+ to be nil
12
+ #
13
+ # @param [Class] klazz the class that +it+ must be the if +it+ is not NilClass
14
+ # @return [CaseEqualityWithList]
15
+ def AllowNil(klazz) # rubocop:disable Naming/MethodName
16
+ AnyOf(NilClass, klazz)
17
+ end
18
+ end
19
+
20
+ # noinspection RubyInstanceMethodNamingConvention
21
+ ##
22
+ # Allows the wrapped +klazz+ to be nil
23
+ #
24
+ # @param [Class] klazz the class that +it+ must be the if +it+ is not NilClass
25
+ # @return [CaseEqualityWithList]
26
+ def AllowNil(klazz) # rubocop:disable Naming/MethodName
27
+ self.class.AllowNil(klazz)
28
+ end
29
+ end
30
+ end