media_types 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8285f897870c9a66139c5129db60c7abf31f870ef20103e6d21da6be99c139b8
4
- data.tar.gz: a62bbb54c1e1de59cdbef2c98050f1aa0d040dc6aa15a7559aa8e016014d26b0
3
+ metadata.gz: d77ce12d19694083cf37f19ccf0c68b402abe672c5e50b0d322879e9600ebda2
4
+ data.tar.gz: d2f90701ea7c0caf572e7565dcb47ff144ecc9255c81dadbe3d8264b6fcf1b30
5
5
  SHA512:
6
- metadata.gz: 132ca0f0f8acf1dcd915c75c7b7b283ee3cc3a896d3fb53acffd8adc958300535bf7dde1d030e26865b317905c5da62ad18e507dbb1f04829ef5582bd4b187b3
7
- data.tar.gz: ee0d5bd2f3e51f492d5530bbfa45e1acfb067ebc2a690a4b4cdf02d1284792bed60eebfaa6757c0b8a36fb520566932da784c9434d235fa59fb7c58f97231da3
6
+ metadata.gz: 273d09cee3e58708829d3d198d5178a88e86f79bd8ac414ba43587e0af756e1cf63dea66e4e9c4c98c1385b6b87363fabde907962417f24768b34168f1483c11
7
+ data.tar.gz: 0fafe409b8b89eeb979177514c8e25df1641c90a165f88c577e2f933ddc3d858b3c46f7c17672fb96ae2f3b0244bae40824ac53e66c7a46b87dcb206be8e65b4
data/.gitignore CHANGED
@@ -1,10 +1,10 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
- /tmp/
9
-
10
- .idea/
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ .idea/
data/CHANGELOG.md CHANGED
@@ -1,4 +1,13 @@
1
+ # 2.0.0
2
+
3
+ - Removed ability to set default suffix. All suffixes now default to `+json`.
4
+ - Suffixes are now set for a given view and version instead of as a block.
5
+ - Added `suffix :yoursuffix` command to override the default `:json` suffix.
6
+ - Removed defaults block.
7
+ - Removed registrations block.
8
+
1
9
  # 1.0.0
10
+
2
11
  - Added the ability to do inline tests when defining validations using `assert_pass '<json>'` and `assert_fail '<json>'`.
3
12
  - `media_type` has been replaced with `use_name`.
4
13
  - It is no longer possible to set a default version. Please use `version <x> do` instead.
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
- source "https://rubygems.org"
2
-
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
-
5
- # Specify your gem's dependencies in media_types.gemspec
6
- gemspec
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in media_types.gemspec
6
+ gemspec
data/Gemfile.lock CHANGED
@@ -1,63 +1,34 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- media_types (1.0.0)
4
+ media_types (2.0.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- actionpack (5.2.4)
10
- actionview (= 5.2.4)
11
- activesupport (= 5.2.4)
12
- rack (~> 2.0)
13
- rack-test (>= 0.6.3)
14
- rails-dom-testing (~> 2.0)
15
- rails-html-sanitizer (~> 1.0, >= 1.0.2)
16
- actionview (5.2.4)
17
- activesupport (= 5.2.4)
18
- builder (~> 3.1)
19
- erubi (~> 1.4)
20
- rails-dom-testing (~> 2.0)
21
- rails-html-sanitizer (~> 1.0, >= 1.0.3)
22
- activesupport (5.2.4)
23
- concurrent-ruby (~> 1.0, >= 1.0.2)
24
- i18n (>= 0.7, < 2)
25
- minitest (~> 5.1)
26
- tzinfo (~> 1.1)
27
9
  addressable (2.7.0)
28
10
  public_suffix (>= 2.0.2, < 5.0)
29
11
  ansi (1.5.0)
30
12
  awesome_print (1.8.0)
31
- builder (3.2.3)
32
- concurrent-ruby (1.1.5)
33
- crass (1.0.5)
13
+ builder (3.2.4)
34
14
  docile (1.3.2)
35
15
  domain_name (0.5.20190701)
36
16
  unf (>= 0.0.5, < 1.0.0)
37
- erubi (1.9.0)
38
- ffi (1.11.1)
39
- ffi (1.11.1-x64-mingw32)
17
+ ffi (1.12.2)
40
18
  ffi-compiler (1.0.1)
41
19
  ffi (>= 1.0.0)
42
20
  rake
43
- http (4.2.0)
21
+ http (4.3.0)
44
22
  addressable (~> 2.3)
45
23
  http-cookie (~> 1.0)
46
- http-form_data (~> 2.0)
24
+ http-form_data (~> 2.2)
47
25
  http-parser (~> 1.2.0)
48
26
  http-cookie (1.0.3)
49
27
  domain_name (~> 0.5)
50
- http-form_data (2.1.1)
28
+ http-form_data (2.2.0)
51
29
  http-parser (1.2.1)
52
30
  ffi-compiler (>= 1.0, < 2.0)
53
- i18n (1.7.0)
54
- concurrent-ruby (~> 1.0)
55
- json (2.2.0)
56
- loofah (2.4.0)
57
- crass (~> 1.0.2)
58
- nokogiri (>= 1.5.9)
59
- mini_portile2 (2.4.0)
60
- minitest (5.13.0)
31
+ minitest (5.14.0)
61
32
  minitest-ci (3.4.0)
62
33
  minitest (>= 5.0.6)
63
34
  minitest-reporters (1.4.2)
@@ -65,50 +36,30 @@ GEM
65
36
  builder
66
37
  minitest (>= 5.0)
67
38
  ruby-progressbar
68
- nokogiri (1.10.7)
69
- mini_portile2 (~> 2.4.0)
70
- nokogiri (1.10.7-x64-mingw32)
71
- mini_portile2 (~> 2.4.0)
72
- oj (3.10.0)
73
- public_suffix (4.0.1)
74
- rack (2.0.7)
75
- rack-test (1.1.0)
76
- rack (>= 1.0, < 3)
77
- rails-dom-testing (2.0.3)
78
- activesupport (>= 4.2.0)
79
- nokogiri (>= 1.6)
80
- rails-html-sanitizer (1.3.0)
81
- loofah (~> 2.3)
39
+ public_suffix (4.0.3)
82
40
  rake (13.0.1)
83
41
  ruby-progressbar (1.10.1)
84
- simplecov (0.17.1)
42
+ simplecov (0.18.5)
85
43
  docile (~> 1.1)
86
- json (>= 1.8, < 3)
87
- simplecov-html (~> 0.10.0)
88
- simplecov-html (0.10.2)
89
- thread_safe (0.3.6)
90
- tzinfo (1.2.5)
91
- thread_safe (~> 0.1)
44
+ simplecov-html (~> 0.11)
45
+ simplecov-html (0.12.2)
92
46
  unf (0.1.4)
93
47
  unf_ext
94
48
  unf_ext (0.0.7.6)
95
49
 
96
50
  PLATFORMS
97
51
  ruby
98
- x64-mingw32
99
52
 
100
53
  DEPENDENCIES
101
- actionpack
102
54
  awesome_print
103
- bundler (~> 2)
55
+ bundler
104
56
  http
105
57
  media_types!
106
58
  minitest (~> 5.0)
107
59
  minitest-ci
108
60
  minitest-reporters
109
- oj
110
61
  rake (~> 13.0)
111
62
  simplecov
112
63
 
113
64
  BUNDLED WITH
114
- 2.0.1
65
+ 1.17.3
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # MediaTypes
2
- [![Build Status](https://travis-ci.com/SleeplessByte/media-types-ruby.svg?branch=master)](https://travis-ci.com/SleeplessByte/media-types-ruby)
2
+ [![Build Status](https://github.com/SleeplessByte/media-types-ruby/workflows/Ruby/badge.svg?branch=master)](https://github.com/SleeplessByte/media-types-ruby/actions?query=workflow%3ARuby)
3
3
  [![Gem Version](https://badge.fury.io/rb/media_types.svg)](https://badge.fury.io/rb/media_types)
4
4
  [![MIT license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT)
5
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/6f2dc1fb37ecb98c4363/maintainability)](https://codeclimate.com/github/SleeplessByte/media-types-ruby/maintainability)
@@ -64,7 +64,7 @@ class Venue
64
64
  'mydomain'
65
65
  end
66
66
 
67
- media_type 'venue', defaults: { suffix: :json }
67
+ use_name 'venue'
68
68
 
69
69
  validations do
70
70
  version 2 do
@@ -103,17 +103,6 @@ class Venue
103
103
  end
104
104
  end
105
105
  end
106
-
107
- registrations :venue_json do
108
- view 'create', :create_venue
109
- view 'index', :venue_urls
110
- view 'collection', :venue_collection
111
-
112
- versions [1,2]
113
-
114
- suffix :json
115
- suffix :xml
116
- end
117
106
  end
118
107
  ```
119
108
 
@@ -346,9 +335,6 @@ Venue.mime_type.identifier
346
335
  Venue.mime_type.version(1).identifier
347
336
  # => "application/vnd.mydomain.venue.v1+json"
348
337
 
349
- Venue.mime_type.version(1).suffix(:xml).identifier
350
- # => "application/vnd.mydomain.venue.v1+xml"
351
-
352
338
  Venue.mime_type.to_s(0.2)
353
339
  # => "application/vnd.mydomain.venue.v2+json; q=0.2"
354
340
 
@@ -359,43 +345,6 @@ Venue.mime_type.view('active').identifier
359
345
  # => "application/vnd.mydomain.venue.v2.active+json"
360
346
  ```
361
347
 
362
- ## Integrations
363
- The integrations are not loaded by default, so you need to require them:
364
- ```ruby
365
- # For Rails / ActionPack
366
- require 'media_types/integrations/actionpack'
367
-
368
- # For HTTP.rb
369
- require 'media_types/integrations/http'
370
- ```
371
-
372
- Define a `registrations` block on your media type, indicating the symbol for the base type (`registrations :symbol do`) and inside use the registrations dsl to define which media types to register. `versions array_of_numbers` determines which versions, `suffix name` adds a suffix, `type_alias name` adds an alias and `view name, symbol` adds a view.
373
-
374
- ```Ruby
375
- Venue.register
376
- ```
377
-
378
- ### Rails
379
- Load the `actionpack` integration and call `.register` on all the media types you want to be available in Rails. You can do this in the `mime_types` initializer, or anywhere before your controllers are instantiated. Yes, the symbol (by default `<type>_v<version>_<suffix>`) can now be used in your `format` blocks, or as extension in the url.
380
-
381
- Rails only has a default serializer for `application/json`, and content with your `+json` media types (or different once) will not be deserialized by default. A way to overcome this is to set the JSON parameter parser for all new symbols. `.register` gives you back an array of `Registerable` objects that responds to `#to_sym` to get that symbol.
382
-
383
- ```ruby
384
- symbols = Venue.register.map(&:to_sym)
385
-
386
- original_parsers = ActionDispatch::Request.parameter_parsers
387
- new_parser = original_parsers[Mime[:json].symbol]
388
- new_parsers = original_parsers.merge(Hash[*symbols.map { |s| [s, new_parser] }])
389
- ActionDispatch::Request.parameter_parsers = new_parsers
390
- ```
391
-
392
- If you want to validate the content-type and not have your errors be `Rack::Error` but be handled by your controllers, leave this out and add a `before_action` to your controller that deserializes + validates for you.
393
-
394
- ### HTTP.rb
395
- Load the `http` integration and call `.register` on all media types you want to be able to serialize and deserialize. The media type validations will run both before serialization and after deserialization.
396
-
397
- Currently uses `oj` under the hood and this can not be changed.
398
-
399
348
  ## API
400
349
 
401
350
  A defined schema has the following functions available:
data/Rakefile CHANGED
@@ -1,12 +1,12 @@
1
- # frozen_string_literal: true
2
-
3
- require 'bundler/gem_tasks'
4
- require 'rake/testtask'
5
-
6
- Rake::TestTask.new(:test) do |t|
7
- t.libs << 'test'
8
- t.libs << 'lib'
9
- t.test_files = FileList['test/**/*_test.rb']
10
- end
11
-
12
- task default: :test
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ end
11
+
12
+ task default: :test
data/lib/media_types.rb CHANGED
@@ -9,7 +9,6 @@ require 'media_types/scheme'
9
9
  require 'media_types/dsl'
10
10
 
11
11
  require 'media_types/views'
12
- require 'media_types/integrations'
13
12
 
14
13
  module MediaTypes
15
14
  def self.set_organisation(mod, organisation)
@@ -28,11 +28,6 @@ module MediaTypes
28
28
  with(view: view)
29
29
  end
30
30
 
31
- def suffix(suffix = NO_ARG)
32
- return opts[:suffix] if suffix == NO_ARG
33
- with(suffix: suffix)
34
- end
35
-
36
31
  def collection
37
32
  view(COLLECTION_VIEW)
38
33
  end
@@ -74,13 +69,18 @@ module MediaTypes
74
69
  end
75
70
 
76
71
  def as_key
77
- [type, view, version, suffix]
72
+ [type, view, version]
78
73
  end
79
74
 
80
75
  def hash
81
76
  as_key.hash
82
77
  end
83
78
 
79
+ def suffix
80
+ schema = schema_for(self)
81
+ schema.type_attributes.fetch(:suffix, 'json')
82
+ end
83
+
84
84
  def to_str(qualifier = nil)
85
85
  qualified(
86
86
  qualifier,
@@ -88,7 +88,7 @@ module MediaTypes
88
88
  type: opts[:type],
89
89
  view: opts[:view],
90
90
  version: opts[:version],
91
- suffix: opts[:suffix],
91
+ suffix: suffix
92
92
  )
93
93
  )
94
94
  end
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'media_types/constructable'
4
- require 'media_types/defaults'
5
- require 'media_types/registrar'
6
4
  require 'media_types/validations'
7
5
 
8
6
  module MediaTypes
@@ -49,7 +47,9 @@ module MediaTypes
49
47
  def validatable?(media_type = to_constructable)
50
48
  return false unless validations
51
49
 
52
- validations.find(media_type, -> { nil })
50
+ resolved = validations.find(media_type, -> { nil })
51
+
52
+ !resolved.nil?
53
53
  end
54
54
 
55
55
  def register
@@ -65,10 +65,7 @@ module MediaTypes
65
65
  def version(v)
66
66
  to_constructable.version(v)
67
67
  end
68
- def suffix(s)
69
- to_constructable.suffix(s)
70
- end
71
-
68
+
72
69
  def identifier_format
73
70
  self.media_type_name_for = Proc.new do |type:, view:, version:, suffix:|
74
71
  yield(type: type, view: view, version: version, suffix: suffix)
@@ -81,14 +78,18 @@ module MediaTypes
81
78
 
82
79
  def available_validations
83
80
  self.media_type_combinations.map do |a|
84
- _, view, version, suffix = a
85
- view(view).version(version).suffix(suffix)
81
+ _, view, version = a
82
+ view(view).version(version)
86
83
  end
87
84
  end
88
85
 
86
+ def schema_for(constructable)
87
+ validations.find(constructable)
88
+ end
89
+
89
90
  private
90
91
 
91
- def use_name(name, defaults: {})
92
+ def use_name(name)
92
93
  if self.media_type_name_for.nil?
93
94
  self.media_type_name_for = Proc.new do |type:, view:, version:, suffix:|
94
95
  resolved_org = nil
@@ -109,25 +110,14 @@ module MediaTypes
109
110
  result
110
111
  end
111
112
  end
112
- self.media_type_constructable = Constructable.new(self, type: name).suffix(defaults.fetch(:suffix) { nil })
113
- end
114
-
115
- def defaults(&block)
116
- return media_type_constructable unless block_given?
117
- self.media_type_constructable = Defaults.new(to_constructable, &block).to_constructable
118
-
119
- self
120
- end
121
-
122
- def registrations(symbol = nil, &block)
123
- return media_type_registrar unless block_given?
124
- self.media_type_registrar = Registrar.new(self, symbol: symbol, &block)
125
-
126
- self
113
+ self.media_type_constructable = Constructable.new(self, type: name)
127
114
  end
128
115
 
129
116
  def validations(&block)
130
- return media_type_validations unless block_given?
117
+ unless block_given?
118
+ raise 'No validations defined' if media_type_validations.nil?
119
+ return media_type_validations
120
+ end
131
121
  self.media_type_validations = Validations.new(to_constructable, &block)
132
122
 
133
123
  self
@@ -56,10 +56,13 @@ module MediaTypes
56
56
  #
57
57
  def initialize(allow_empty: false, expected_type: ::Object, &block)
58
58
  self.rules = Rules.new(allow_empty: allow_empty, expected_type: expected_type)
59
+ self.type_attributes = {}
59
60
 
60
61
  instance_exec(&block) if block_given?
61
62
  end
62
63
 
64
+ attr_accessor :type_attributes
65
+
63
66
  ##
64
67
  # Checks if the +output+ is valid
65
68
  #
@@ -98,6 +101,7 @@ module MediaTypes
98
101
  #
99
102
  def validate(output, options = nil, **opts)
100
103
  options ||= ValidationOptions.new(**opts)
104
+ options.context = output
101
105
 
102
106
  catch(:end) do
103
107
  validate!(output, options, context: nil)
@@ -9,18 +9,21 @@ module MediaTypes
9
9
  def validate!(_output, options, context:, **_opts)
10
10
  # Check that no unknown keys are present
11
11
  return true unless options.strict
12
- raise_strict!(key: context.key, strict_keys: context.rules, backtrace: options.backtrace)
12
+ raise_strict!(key: context.key, strict_keys: context.rules, backtrace: options.backtrace, found: options.scoped_output)
13
13
  end
14
14
 
15
- def raise_strict!(key:, backtrace:, strict_keys:)
15
+ def raise_strict!(key:, backtrace:, strict_keys:, found:)
16
16
  raise StrictValidationError, format(
17
17
  "Unknown key %<key>s in data.\n" \
18
18
  "\tFound at: %<backtrace>s\n" \
19
19
  "\tExpected:\n\n" \
20
- '%<strict_keys>s',
20
+ "%<strict_keys>s\n\n" \
21
+ "\tBut I Found:\n\n" \
22
+ '%<found>s',
21
23
  key: key.inspect,
22
24
  backtrace: backtrace.join('->'),
23
- strict_keys: strict_keys.inspect(1)
25
+ strict_keys: strict_keys.keys,
26
+ found: (found.is_a? Hash) ? found.keys : found.class.name
24
27
  )
25
28
  end
26
29
 
@@ -21,7 +21,7 @@ module MediaTypes
21
21
  def call
22
22
  return unless MediaTypes::Object.new(output).empty?
23
23
  throw(:end, true) if allow_empty?
24
- raise_empty!(backtrace: options.backtrace)
24
+ raise_empty!(backtrace: options.backtrace, found: options.scoped_output)
25
25
  end
26
26
 
27
27
  private
@@ -32,11 +32,12 @@ module MediaTypes
32
32
  rules.allow_empty? || rules.required.empty?
33
33
  end
34
34
 
35
- def raise_empty!(backtrace:)
35
+ def raise_empty!(backtrace:, found:)
36
36
  raise EmptyOutputError, format(
37
- 'Expected output, got empty at %<backtrace>s. Required are: %<required>s.',
37
+ 'Expected output, got empty at %<backtrace>s. Required are: %<required>s. Found: %<found>s',
38
38
  backtrace: backtrace.join('->'),
39
- required: rules.required.keys
39
+ required: rules.required.keys,
40
+ found: (found.is_a? Hash) ? found.keys : found.class.name,
40
41
  )
41
42
  end
42
43
  end
@@ -31,7 +31,7 @@ module MediaTypes
31
31
  result = iterate(->(key) { required_rules.remove(key) })
32
32
  return result if required_rules.empty?
33
33
 
34
- raise_exhausted!(missing_keys: required_rules.keys, backtrace: options.backtrace)
34
+ raise_exhausted!(missing_keys: required_rules.keys, backtrace: options.backtrace, found: output)
35
35
  end
36
36
 
37
37
  def iterate(mark)
@@ -50,11 +50,12 @@ module MediaTypes
50
50
 
51
51
  attr_accessor :rules, :options, :output
52
52
 
53
- def raise_exhausted!(missing_keys:, backtrace:)
53
+ def raise_exhausted!(missing_keys:, backtrace:, found:)
54
54
  raise ExhaustedOutputError, format(
55
- 'Missing keys in output: %<missing_keys>s at [%<backtrace>s]',
55
+ 'Missing keys in output: %<missing_keys>s at [%<backtrace>s]. I did find: %<found>s',
56
56
  missing_keys: missing_keys,
57
- backtrace: backtrace.join('->')
57
+ backtrace: backtrace.join('->'),
58
+ found: (found.is_a? Hash) ? found.keys : found.class.name,
58
59
  )
59
60
  end
60
61
  end
@@ -3,20 +3,31 @@
3
3
  module MediaTypes
4
4
  class Scheme
5
5
  class ValidationOptions
6
- attr_accessor :exhaustive, :strict, :backtrace
6
+ attr_accessor :exhaustive, :strict, :backtrace, :context
7
7
 
8
- def initialize(exhaustive: true, strict: true, backtrace: [])
8
+ def initialize(context = {}, exhaustive: true, strict: true, backtrace: [])
9
9
  self.exhaustive = exhaustive
10
10
  self.strict = strict
11
11
  self.backtrace = backtrace
12
+ self.context = context
12
13
  end
13
14
 
14
15
  def inspect
15
- "backtrack: #{backtrace.inspect}, strict: #{strict.inspect}, exhaustive: #{exhaustive}"
16
+ "backtrack: #{backtrace.inspect}, strict: #{strict.inspect}, exhaustive: #{exhaustive}, current_obj: #{scoped_output.to_json}"
17
+ end
18
+
19
+ def scoped_output
20
+ current = context
21
+
22
+ backtrace.drop(1).first([0, backtrace.size - 2].max).each do |e|
23
+ current = current[e] unless current.nil?
24
+ end
25
+
26
+ current
16
27
  end
17
28
 
18
29
  def with_backtrace(backtrace)
19
- ValidationOptions.new(exhaustive: exhaustive, strict: strict, backtrace: backtrace)
30
+ ValidationOptions.new(context, exhaustive: exhaustive, strict: strict, backtrace: backtrace)
20
31
  end
21
32
 
22
33
  def trace(*traces)
@@ -24,7 +35,7 @@ module MediaTypes
24
35
  end
25
36
 
26
37
  def exhaustive!
27
- ValidationOptions.new(exhaustive: true, strict: strict, backtrace: backtrace)
38
+ ValidationOptions.new(context, exhaustive: true, strict: strict, backtrace: backtrace)
28
39
  end
29
40
  end
30
41
  end
@@ -46,6 +46,7 @@ module MediaTypes
46
46
 
47
47
  def method_missing(method_name, *arguments, &block)
48
48
  if scheme.respond_to?(method_name)
49
+ media_type.__getobj__.media_type_combinations ||= Set.new
49
50
  media_type.__getobj__.media_type_combinations.add(media_type.as_key)
50
51
 
51
52
  return scheme.send(method_name, *arguments, &block)
@@ -79,5 +80,9 @@ module MediaTypes
79
80
  def view(view, &block)
80
81
  Validations.new(media_type.view(view), registry, &block)
81
82
  end
83
+
84
+ def suffix(name)
85
+ scheme.type_attributes[:suffix] = name
86
+ end
82
87
  end
83
88
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MediaTypes
4
- VERSION = '1.0.0'
4
+ VERSION = '2.0.0'
5
5
  end
data/media_types.gemspec CHANGED
@@ -7,8 +7,8 @@ require 'media_types/version'
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = 'media_types'
9
9
  spec.version = MediaTypes::VERSION
10
- spec.authors = ['Derk-Jan Karrenbeld']
11
- spec.email = ['derk-jan+github@karrenbeld.info']
10
+ spec.authors = ['Derk-Jan Karrenbeld', 'Max Maton']
11
+ spec.email = ['derk-jan+github@karrenbeld.info', 'info@maxmaton.nl']
12
12
 
13
13
  spec.summary = 'Library to create media type definitions, schemes and validations'
14
14
  spec.description = 'Media Types as mime types are not easily supported by frameworks such as rails. '
@@ -23,14 +23,12 @@ Gem::Specification.new do |spec|
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = ['lib']
25
25
 
26
- spec.add_development_dependency 'actionpack'
27
26
  spec.add_development_dependency 'awesome_print'
28
- spec.add_development_dependency 'bundler', '~> 2'
27
+ spec.add_development_dependency 'bundler'
29
28
  spec.add_development_dependency 'http'
30
29
  spec.add_development_dependency 'minitest', '~> 5.0'
31
30
  spec.add_development_dependency 'minitest-ci'
32
31
  spec.add_development_dependency 'minitest-reporters'
33
- spec.add_development_dependency 'oj'
34
32
  spec.add_development_dependency 'rake', '~> 13.0'
35
33
  spec.add_development_dependency 'simplecov'
36
34
  end
metadata CHANGED
@@ -1,17 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: media_types
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derk-Jan Karrenbeld
8
+ - Max Maton
8
9
  autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2020-01-21 00:00:00.000000000 Z
12
+ date: 2020-03-28 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- name: actionpack
15
+ name: awesome_print
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
18
  - - ">="
@@ -25,7 +26,7 @@ dependencies:
25
26
  - !ruby/object:Gem::Version
26
27
  version: '0'
27
28
  - !ruby/object:Gem::Dependency
28
- name: awesome_print
29
+ name: bundler
29
30
  requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
32
  - - ">="
@@ -38,20 +39,6 @@ dependencies:
38
39
  - - ">="
39
40
  - !ruby/object:Gem::Version
40
41
  version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: bundler
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '2'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '2'
55
42
  - !ruby/object:Gem::Dependency
56
43
  name: http
57
44
  requirement: !ruby/object:Gem::Requirement
@@ -108,20 +95,6 @@ dependencies:
108
95
  - - ">="
109
96
  - !ruby/object:Gem::Version
110
97
  version: '0'
111
- - !ruby/object:Gem::Dependency
112
- name: oj
113
- requirement: !ruby/object:Gem::Requirement
114
- requirements:
115
- - - ">="
116
- - !ruby/object:Gem::Version
117
- version: '0'
118
- type: :development
119
- prerelease: false
120
- version_requirements: !ruby/object:Gem::Requirement
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- version: '0'
125
98
  - !ruby/object:Gem::Dependency
126
99
  name: rake
127
100
  requirement: !ruby/object:Gem::Requirement
@@ -154,6 +127,7 @@ description: 'Media Types as mime types are not easily supported by frameworks s
154
127
  as rails. '
155
128
  email:
156
129
  - derk-jan+github@karrenbeld.info
130
+ - info@maxmaton.nl
157
131
  executables: []
158
132
  extensions: []
159
133
  extra_rdoc_files: []
@@ -161,7 +135,6 @@ files:
161
135
  - ".github/workflows/ruby.yml"
162
136
  - ".gitignore"
163
137
  - ".rubocop.yml"
164
- - ".travis.yml"
165
138
  - CHANGELOG.md
166
139
  - Gemfile
167
140
  - Gemfile.lock
@@ -170,19 +143,12 @@ files:
170
143
  - bin/console
171
144
  - bin/setup
172
145
  - lib/media_types.rb
173
- - lib/media_types/.dsl.rb.swp
174
146
  - lib/media_types/constructable.rb
175
- - lib/media_types/defaults.rb
176
147
  - lib/media_types/dsl.rb
177
148
  - lib/media_types/formatter.rb
178
149
  - lib/media_types/hash.rb
179
- - lib/media_types/integrations.rb
180
- - lib/media_types/integrations/actionpack.rb
181
- - lib/media_types/integrations/http.rb
182
150
  - lib/media_types/minitest/assert_media_type_format.rb
183
- - lib/media_types/minitest/assert_media_types_registered.rb
184
151
  - lib/media_types/object.rb
185
- - lib/media_types/registrar.rb
186
152
  - lib/media_types/scheme.rb
187
153
  - lib/media_types/scheme/allow_nil.rb
188
154
  - lib/media_types/scheme/any_of.rb
data/.travis.yml DELETED
@@ -1,19 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.4
6
- - 2.5
7
- - 2.6
8
- - rbx-3
9
- - ruby-head
10
- matrix:
11
- allow_failures:
12
- - rvm: ruby-head
13
- - rvm: rbx-3
14
- - rvm: 2.6
15
- before_install:
16
- - gem update --system
17
- - gem --version
18
- install:
19
- - bundle install --with development --jobs=3 --retry=3 --path=${BUNDLE_PATH:-vendor/bundle}
Binary file
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MediaTypes
4
- class Defaults
5
- def initialize(media_type, &block)
6
- self.media_type = media_type
7
-
8
- instance_exec(&block) if block_given?
9
- end
10
-
11
- def method_missing(method_name, *arguments, &block)
12
- if media_type.respond_to?(method_name)
13
- return self.media_type = media_type.send(method_name, *arguments, &block)
14
- end
15
-
16
- super
17
- end
18
-
19
- def respond_to_missing?(method_name, include_private = false)
20
- media_type.respond_to?(method_name) || super
21
- end
22
-
23
- def to_constructable
24
- media_type
25
- end
26
-
27
- private
28
-
29
- attr_accessor :media_type
30
- end
31
- end
@@ -1,32 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MediaTypes
4
- INTEGRATION_METHODS = %i[register].freeze
5
-
6
- module_function
7
-
8
- def integrate(integration)
9
- INTEGRATION_METHODS.each do |method|
10
- next unless integration.respond_to?(method)
11
- self.integrations = (integrations || {}).tap do |x|
12
- x.merge!(method => (x[method] || []).concat([integration]))
13
- end
14
- end
15
- end
16
-
17
- # @!method register(registerable)
18
- INTEGRATION_METHODS.each do |method|
19
- define_singleton_method method do |*args, &block|
20
- (integrations || {}).fetch(method) { [] }.each do |integration|
21
- integration.send(method, *args, &block)
22
- end
23
- end
24
-
25
- end
26
-
27
- class << self
28
- private
29
-
30
- attr_accessor :integrations
31
- end
32
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'action_dispatch/http/mime_type'
4
-
5
- module MediaTypes
6
- module ActionPackIntegration
7
-
8
- module_function
9
-
10
- def register(registerable)
11
- mime_type = registerable.to_s
12
- symbol = registerable.to_sym
13
- synonyms = registerable.aliases
14
-
15
- Mime::Type.register(mime_type, symbol, synonyms)
16
- end
17
- end
18
-
19
- integrate ActionPackIntegration
20
- end
21
-
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'http/mime_type'
4
- require 'oj'
5
-
6
- module MediaTypes
7
- module HttpIntegration
8
-
9
- module_function
10
-
11
- def register(registerable)
12
- mime_type = registerable.to_s
13
-
14
- HTTP::MimeType.register_adapter mime_type, AdapterFor(registerable.media_type)
15
- HTTP::MimeType.register_alias mime_type, registerable.to_sym
16
-
17
- registerable.aliases.each do |alias_mime_type|
18
- HTTP::MimeType.register_alias mime_type, alias_mime_type
19
- end
20
- end
21
-
22
- class << self
23
- private
24
-
25
- # noinspection RubyInstanceMethodNamingConvention
26
- def AdapterFor(media_type) # rubocop:disable Naming/MethodName
27
- adapter_name = media_type.split(%r{[./_+-]}).map(&:capitalize).join('').tr('^A-z0-9', '_')
28
-
29
- adapter = MediaTypes::HttpIntegration.const_set(adapter_name, Module.new)
30
- adapter.define_singleton_method('encode') do |obj|
31
- media_type.validate!(obj)
32
- Oj.dump(obj, mode: :compat)
33
- end
34
-
35
- adapter.define_singleton_method('decode') do |str|
36
- Oj.load(str, mode: :strict).tap do |result|
37
- media_type.validate!(result)
38
- end
39
- end
40
-
41
- adapter
42
- end
43
- end
44
- end
45
-
46
- integrate HttpIntegration
47
- end
@@ -1,166 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'minitest/mock'
4
-
5
- module MediaTypes
6
- module Assertions
7
- class << self
8
- def block_exec_instance(instance, &block)
9
- case block.arity
10
- when 1, -1
11
- instance.instance_exec(instance, &block)
12
- else
13
- instance.instance_exec(&block)
14
- end
15
- end
16
- end
17
-
18
- def assert_media_types_registered(media_type, &block)
19
- collector = Collector.new(media_type)
20
- Assertions.block_exec_instance(collector, &block)
21
- assert_registered_types(media_type, collector.prepare_verify)
22
- end
23
-
24
- class Collector
25
- def initialize(media_type)
26
- self.media_type = media_type
27
- self.registers = {}
28
- end
29
-
30
- def mime_type(mime_type, symbol:, synonyms: [])
31
- registers[mime_type] = { symbol: symbol, synonyms: synonyms }
32
- end
33
-
34
- def formatted_mime_type(mime_type_format, &block)
35
- collector = FormattedCollector.new(mime_type_format, {})
36
- Assertions.block_exec_instance(collector, &block)
37
- registers.merge!(collector.to_h)
38
- end
39
-
40
- def prepare_verify
41
- expected_types_hash = registers.dup
42
- expected_types_hash.each do |key, value|
43
- expected_types_hash[key] = [value[:symbol], Array(value[:synonyms])]
44
- end
45
-
46
- expected_types_hash
47
- end
48
-
49
- private
50
-
51
- attr_accessor :media_type, :registers
52
- end
53
-
54
- class FormattedCollector
55
- def initialize(format, args = {})
56
- self.mime_type_format = format
57
- self.format_args = args
58
- self.registers = {}
59
- end
60
-
61
- def version(version, **opts, &block)
62
- new_format_args = format_args.merge(version: version)
63
- register(new_format_args, **opts, &block)
64
- end
65
-
66
- def view(view, **opts, &block)
67
- new_format_args = format_args.merge(view: view)
68
- register(new_format_args, **opts, &block)
69
- end
70
-
71
- def create(**opts, &block)
72
- view(CREATE_VIEW, **opts, &block)
73
- end
74
-
75
- def collection(**opts, &block)
76
- view(COLLECTION_VIEW, **opts, &block)
77
- end
78
-
79
- def index(**opts, &block)
80
- view(INDEX_VIEW, **opts, &block)
81
- end
82
-
83
- def to_h
84
- Hash(registers)
85
- end
86
-
87
- private
88
-
89
- attr_accessor :mime_type_format, :registers, :format_args
90
-
91
- def register(new_format_args, symbol: nil, synonyms: [], &block)
92
- if block_given?
93
- collector = FormattedCollector.new(mime_type_format, new_format_args)
94
- Assertions.block_exec_instance(collector, &block)
95
- registers.merge!(collector.to_h)
96
- else
97
- formatted_mime_type_format = format(mime_type_format, **new_format_args)
98
- registers[formatted_mime_type_format] = { symbol: symbol, synonyms: synonyms }
99
- end
100
- end
101
- end
102
-
103
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
104
- def assert_registered_types(media_type, expected_types_hash)
105
- mock = Minitest::Mock.new
106
- uncalled = expected_types_hash.dup
107
-
108
- failed = []
109
-
110
- uncalled.length.times do
111
- mock.expect(:call, nil) do |registerable|
112
- type = registerable.to_s
113
- symbol = registerable.to_sym
114
- synonyms = registerable.synonyms
115
-
116
- options = uncalled.delete(type)
117
- return true if options && options == [symbol, synonyms] && pass
118
-
119
- failed <<
120
- MockExpectationError.new(
121
- format(
122
- 'Call failed to match expectations:' + "\n"\
123
- '+++ actual [type: %<type>s, symbol: %<symbol>s, synonyms: %<synonyms>s]' + "\n"\
124
- '--- expected [type: %<type>s, symbol: %<resolved_symbol>s, synonyms: %<resolved_synonyms>s]',
125
- type: type,
126
- symbol: symbol,
127
- synonyms: synonyms,
128
- resolved_symbol: options&.first,
129
- resolved_synonyms: options&.last
130
- )
131
- )
132
- end
133
-
134
- false
135
- end
136
-
137
- MediaTypes.stub(:register, mock) do
138
- if block_given?
139
- yield media_type
140
- else
141
- media_type.register.flatten
142
- end
143
- end
144
-
145
- messages = failed.map(&:message)
146
- uncalled.each do |type, options|
147
- messages << format(
148
- 'Call did not occur:' + "\n"\
149
- '--- expected: [type: %<type>s, symbol: %<resolved_symbol>s, synonyms: %<resolved_synonyms>s]',
150
- type: type,
151
- resolved_symbol: options&.first,
152
- resolved_synonyms: options&.last
153
- )
154
- end
155
-
156
- if messages.length.positive?
157
- flunk messages.join(",\n")
158
- else
159
- pass
160
- end
161
-
162
- assert mock
163
- end
164
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
165
- end
166
- end
@@ -1,148 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module MediaTypes
4
- class Registerable
5
- def initialize(media_type, symbol:, aliases: [])
6
- self.media_type = media_type
7
- self.symbol = symbol
8
- self.aliases = aliases
9
-
10
- freeze
11
- end
12
-
13
- def to_s
14
- String(media_type)
15
- end
16
-
17
- def to_sym
18
- symbol
19
- end
20
-
21
- attr_accessor :media_type, :symbol, :aliases
22
-
23
- alias synonyms aliases
24
- alias mime_type to_s
25
-
26
- end
27
-
28
- ##
29
- # Holds all the configuration options in order to call {MediaTypes.register} exactly enough times to register the
30
- # media type in its entirety.
31
- #
32
- # @see Registerable
33
- #
34
- class Registrar
35
-
36
- ##
37
- # Creates a new registry with default values
38
- #
39
- # @param [Class] klazz the class that has {MediaTypes::Dsl} included
40
- # @param [Symbol, NilClass] symbol the symbol the base view (nil view) should be registered under
41
- #
42
- def initialize(klazz, symbol: nil, &block)
43
- self.base_media_type = klazz.to_constructable
44
-
45
- self.registered_views = symbol ? { nil => { symbol: String(symbol).to_sym } } : {}
46
- self.registered_versions = [base_media_type.version]
47
- self.registered_aliases = []
48
- self.registered_suffixes = [String(base_media_type.suffix).to_sym]
49
-
50
- instance_exec(&block) if block_given?
51
- end
52
-
53
- ##
54
- # Resolves all the permutations and returns a list of {Registerable}
55
- #
56
- # @return [Array<Registerable>] the registerables based on all the permutations of +self+
57
- #
58
- def to_a
59
- result = []
60
-
61
- each_resolved do |media_type, symbol|
62
- result << Registerable.new(media_type, symbol: symbol, aliases: aliases(media_type))
63
- end
64
-
65
- result
66
- end
67
-
68
- private
69
-
70
- attr_accessor :registered_views, :registered_versions, :registered_aliases, :registered_suffixes,
71
- :symbol_base, :base_media_type
72
-
73
- ##
74
- # Registers a +view+ with a +symbol+
75
- #
76
- # @param [String, Symbol] view the view
77
- # @param [String, Symbol] symbol the symbol base for this view
78
- #
79
- def view(view, symbol)
80
- registered_views[String(view)] = { symbol: String(symbol).to_sym }
81
- self
82
- end
83
-
84
- ##
85
- # Registers +versions+
86
- #
87
- # @param [*Numeric] versions the versions to register
88
- #
89
- def versions(*versions)
90
- registered_versions.push(*versions)
91
- self
92
- end
93
-
94
- ##
95
- # Registers a type alias. This will become a synonym when registering the media type
96
- #
97
- # @param [String] alias_name the name of the alias
98
- #
99
- def type_alias(alias_name)
100
- registered_aliases.push(String(alias_name))
101
- self
102
- end
103
-
104
- ##
105
- # Registers a suffix
106
- #
107
- # @param [String, Symbol] suffix the suffix to register
108
- #
109
- def suffix(suffix)
110
- registered_suffixes.push(String(suffix).to_sym)
111
- self
112
- end
113
-
114
- ##
115
- # Calculates the aliased media types for a media type
116
- #
117
- # @param [Constructable] media_type the media type constructable
118
- # @return [Array<String>] the resolved aliases
119
- #
120
- def aliases(media_type)
121
- registered_aliases.map { |a| media_type.type(a).to_s }
122
- end
123
-
124
- def each_combination
125
- iterable(registered_versions).each do |version|
126
- registered_views.each do |view, view_opts|
127
- iterable(registered_suffixes).each do |suffix|
128
- opts = { view: view_opts }
129
- yield version, view, suffix, opts
130
- end
131
- end
132
- end
133
- end
134
-
135
- def iterable(source)
136
- source.compact.uniq
137
- end
138
-
139
- def each_resolved
140
- each_combination do |version, view, suffix, opts|
141
- media_type = base_media_type.version(version).view(view).suffix(suffix)
142
- symbol = iterable([opts[:view][:symbol], "v#{version}", suffix]).join('_').to_sym
143
-
144
- yield media_type, symbol
145
- end
146
- end
147
- end
148
- end