versioncake 4.0.1 → 4.1.1

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +33 -0
  3. data/Appraisals +9 -3
  4. data/CHANGELOG.md +42 -1
  5. data/Gemfile.lock +26 -24
  6. data/README.md +14 -11
  7. data/SECURITY.md +9 -0
  8. data/gemfiles/rails5.0.gemfile.lock +26 -24
  9. data/gemfiles/rails5.2.gemfile.lock +36 -34
  10. data/gemfiles/rails6.0.gemfile +3 -3
  11. data/gemfiles/rails6.0.gemfile.lock +44 -40
  12. data/gemfiles/rails7.0.gemfile +9 -0
  13. data/gemfiles/rails7.0.gemfile.lock +127 -0
  14. data/lib/versioncake/controller_additions.rb +0 -6
  15. data/lib/versioncake/railtie.rb +9 -0
  16. data/lib/versioncake/response_strategy/http_content_type_strategy.rb +4 -2
  17. data/lib/versioncake/strategies/extraction_strategy.rb +11 -7
  18. data/lib/versioncake/version.rb +1 -1
  19. data/lib/versioncake/version_checker.rb +2 -1
  20. data/lib/versioncake/version_context_service.rb +1 -1
  21. data/lib/versioncake/versioned_request.rb +1 -1
  22. data/lib/versioncake/view_additions.rb +8 -88
  23. data/lib/versioncake/view_additions_rails5.rb +70 -0
  24. data/lib/versioncake/view_additions_rails6.rb +69 -0
  25. data/lib/versioncake/view_additions_rails7.rb +155 -0
  26. data/lib/versioncake.rb +3 -1
  27. data/spec/integration/controller/unversioned_controller_spec.rb +1 -1
  28. data/spec/integration/view/render_spec.rb +8 -1
  29. data/spec/integration/view/view_additions_rails5_spec.rb +67 -0
  30. data/spec/integration/view/view_additions_rails6_spec.rb +44 -0
  31. data/spec/integration/view/view_additions_rails7_spec.rb +76 -0
  32. data/spec/unit/strategies/extraction_strategy_spec.rb +2 -2
  33. data/spec/unit/version_checker_spec.rb +1 -1
  34. data/spec/unit/versioned_request_spec.rb +0 -7
  35. metadata +17 -8
  36. data/.travis.yml +0 -20
  37. data/spec/integration/view/view_additions_spec.rb +0 -42
@@ -0,0 +1,155 @@
1
+ require 'action_view'
2
+
3
+ # register an addition detail for the lookup context to understand,
4
+ # this will allow us to have the versions available upon lookup in
5
+ # the resolver.
6
+ ActionView::LookupContext.register_detail(:versions){ [] }
7
+
8
+ ActionView::Resolver::PathParser.class_eval do
9
+ def build_path_regex
10
+ handlers = ActionView::Template::Handlers.extensions.map { |x| Regexp.escape(x) }.join("|")
11
+ formats = ActionView::Template::Types.symbols.map { |x| Regexp.escape(x) }.join("|")
12
+ locales = "[a-z]{2}(?:-[A-Z]{2})?"
13
+ variants = "[^.]*"
14
+
15
+ %r{
16
+ \A
17
+ (?:(?<prefix>.*)/)?
18
+ (?<partial>_)?
19
+ (?<action>.*?)
20
+ (?:\.(?<locale>#{locales}))??
21
+ (?:\.(?<format>#{formats}))??
22
+ (?:\+(?<variant>#{variants}))??
23
+ (?:\.(?<versions>v[0-9]+))??
24
+ (?:\.(?<handler>#{handlers}))?
25
+ \z
26
+ }x
27
+ end
28
+
29
+ def parse(path)
30
+ @regex ||= build_path_regex
31
+ match = @regex.match(path)
32
+ path = ActionView::TemplatePath.build(match[:action], match[:prefix] || "", !!match[:partial])
33
+ details = ActionView::TemplateDetails.new(
34
+ match[:locale]&.to_sym,
35
+ match[:handler]&.to_sym,
36
+ match[:format]&.to_sym,
37
+ match[:variant]&.to_sym,
38
+ match[:versions]&.to_sym,
39
+ )
40
+
41
+ ActionView::Resolver::PathParser::ParsedPath.new(path, details)
42
+ end
43
+ end
44
+
45
+ ActionView::TemplateDetails::Requested.class_eval do
46
+ attr_reader :locale, :handlers, :formats, :variants, :versions
47
+ attr_reader :locale_idx, :handlers_idx, :formats_idx, :variants_idx, :versions_idx
48
+
49
+ def initialize(locale:, handlers:, formats:, variants:, versions:)
50
+ @locale = locale
51
+ @handlers = handlers
52
+ @formats = formats
53
+ @variants = variants
54
+ @versions = versions
55
+
56
+ @locale_idx = build_idx_hash(locale)
57
+ @handlers_idx = build_idx_hash(handlers)
58
+ @formats_idx = build_idx_hash(formats)
59
+ @versions_idx = build_idx_hash(versions)
60
+ if variants == :any
61
+ @variants_idx = ActionView::TemplateDetails::Requested::ANY_HASH
62
+ else
63
+ @variants_idx = build_idx_hash(variants)
64
+ end
65
+ end
66
+ end
67
+
68
+ ActionView::TemplateDetails.class_eval do
69
+ attr_reader :locale, :handler, :format, :variant, :version
70
+
71
+ def initialize(locale, handler, format, variant, version)
72
+ @locale = locale
73
+ @handler = handler
74
+ @format = format
75
+ @variant = variant
76
+ @version = version
77
+ end
78
+
79
+ def matches?(requested)
80
+ requested.formats_idx[@format] &&
81
+ requested.locale_idx[@locale] &&
82
+ requested.versions_idx[@version] &&
83
+ requested.variants_idx[@variant] &&
84
+ requested.handlers_idx[@handler]
85
+ end
86
+
87
+ def sort_key_for(requested)
88
+ [
89
+ requested.formats_idx[@format],
90
+ requested.locale_idx[@locale],
91
+ requested.versions_idx[@version],
92
+ requested.variants_idx[@variant],
93
+ requested.handlers_idx[@handler]
94
+ ]
95
+ end
96
+ end
97
+
98
+ ActionView::Template.class_eval do
99
+ def initialize(source, identifier, handler, locals:, format: nil, variant: nil, virtual_path: nil, version: nil)
100
+ @source = source
101
+ @identifier = identifier
102
+ @handler = handler
103
+ @compiled = false
104
+ @locals = locals
105
+ @virtual_path = virtual_path
106
+
107
+ @variable = if @virtual_path
108
+ base = @virtual_path.end_with?("/") ? "" : ::File.basename(@virtual_path)
109
+ base =~ /\A_?(.*?)(?:\.\w+)*\z/
110
+ $1.to_sym
111
+ end
112
+
113
+ @format = format
114
+ @version = version
115
+ @variant = variant
116
+ @compile_mutex = Mutex.new
117
+ end
118
+
119
+ def marshal_dump # :nodoc:
120
+ [ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @format, @variant, @version ]
121
+ end
122
+
123
+ def marshal_load(array) # :nodoc:
124
+ @source, @identifier, @handler, @compiled, @locals, @virtual_path, @format, @variant, @version = *array
125
+ @compile_mutex = Mutex.new
126
+ end
127
+
128
+ # the identifier method name filters out numbers,
129
+ # but we want to preserve them for v1 etc.
130
+ # TODO: Consider updating to match current implementation:
131
+ # short_identifier.tr("^a-z_", "_")
132
+ def identifier_method_name #:nodoc:
133
+ short_identifier.gsub(/[^a-z0-9_]/, '_')
134
+ end
135
+ end
136
+
137
+ ActionView::UnboundTemplate.class_eval do
138
+ delegate :version, to: :@details
139
+
140
+ def build_template(locals)
141
+ ActionView::Template.new(
142
+ @source,
143
+ @identifier,
144
+ details.handler_class,
145
+
146
+ format: details.format_or_default,
147
+ variant: variant&.to_s,
148
+ virtual_path: @virtual_path,
149
+ version: version&.to_s,
150
+
151
+ locals: locals.map(&:to_s)
152
+ )
153
+ end
154
+ end
155
+
data/lib/versioncake.rb CHANGED
@@ -26,6 +26,7 @@ if defined?(Rails)
26
26
  require 'versioncake/controller_additions'
27
27
  require 'versioncake/view_additions'
28
28
  require 'versioncake/engine'
29
+ require 'versioncake/railtie'
29
30
  end
30
31
 
31
32
  module VersionCake
@@ -38,4 +39,5 @@ module VersionCake
38
39
  def self.setup
39
40
  yield self.config
40
41
  end
41
- end
42
+ end
43
+
@@ -6,7 +6,7 @@ describe UnversionedController, type: :controller do
6
6
  context '#index' do
7
7
  render_views
8
8
 
9
- it { expect(response).to be_success }
9
+ it { expect(response).to be_successful }
10
10
  it { expect(response.body).to eq 'unversioned index' }
11
11
  end
12
12
  end
@@ -3,7 +3,14 @@ require './spec/rails_helper'
3
3
  describe ActionView::Base do
4
4
  let(:path) { ActionView::FileSystemResolver.new('./spec/fixtures') }
5
5
  let(:view_paths) { ActionView::PathSet.new([path]) }
6
- let(:view) { ActionView::Base.new(view_paths) }
6
+ let(:view) do
7
+ clazz = if ActionPack::VERSION::MAJOR >= 7
8
+ ActionView::Base.with_empty_template_cache
9
+ else
10
+ ActionView::Base
11
+ end
12
+ clazz.new(ActionView::LookupContext.new(view_paths), [], nil)
13
+ end
7
14
  let(:version_override) { nil }
8
15
  subject { view.render(template: 'templates/versioned', versions: version_override) }
9
16
 
@@ -0,0 +1,67 @@
1
+ require './spec/rails_helper'
2
+
3
+ if ActionPack::VERSION::MAJOR == 5
4
+ describe ActionView::PathResolver do
5
+ let(:resolver) { ActionView::PathResolver.new }
6
+
7
+ context '#extract_handler_and_format_and_variant' do
8
+ subject do
9
+ resolver.extract_handler_and_format_and_variant("application.#{extension}")
10
+ end
11
+
12
+ let(:variant) { subject[2].to_s }
13
+ let(:format) { subject[1].to_s }
14
+ let(:handler) { subject[0] }
15
+
16
+ context 'when only handler and format are present' do
17
+ let(:extension) { 'html.erb' }
18
+
19
+ it do
20
+ expect(format).to eq 'text/html'
21
+ expect(variant).to be_empty
22
+ expect(handler).to be_a ActionView::Template::Handlers::ERB
23
+ end
24
+ end
25
+
26
+ context 'when handler, format and version are present' do
27
+ let(:extension) { 'json.v1.jbuilder' }
28
+
29
+ it do
30
+ expect(format).to eq 'application/json'
31
+ expect(variant).to be_empty
32
+ expect(handler).to be_a ActionView::Template::Handlers::Raw
33
+ end
34
+ end
35
+
36
+ context 'when handler, format and locale are present' do
37
+ let(:extension) { 'en.json.jbuilder' }
38
+
39
+ it do
40
+ expect(format).to eq 'application/json'
41
+ expect(variant).to be_empty
42
+ expect(handler).to be_a ActionView::Template::Handlers::Raw
43
+ end
44
+ end
45
+
46
+ context 'when handler, format, locale and version are present' do
47
+ let(:extension) { 'en.json.v1.jbuilder' }
48
+
49
+ it do
50
+ expect(format).to eq 'application/json'
51
+ expect(variant).to be_empty
52
+ expect(handler).to be_a ActionView::Template::Handlers::Raw
53
+ end
54
+ end
55
+
56
+ context 'when handler, format, variant and version are present' do
57
+ let(:extension) { 'json+tablet.v1.jbuilder' }
58
+
59
+ it do
60
+ expect(format).to eq 'application/json'
61
+ expect(variant).to eq 'tablet'
62
+ expect(handler).to be_a ActionView::Template::Handlers::Raw
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,44 @@
1
+ require './spec/rails_helper'
2
+
3
+ if ActionPack::VERSION::MAJOR == 6
4
+ describe ActionView::PathResolver do
5
+ let(:resolver) { ActionView::PathResolver.new }
6
+
7
+ context '#extract_handler_and_format' do
8
+ subject(:template_format) do
9
+ _, format = resolver.extract_handler_and_format("application.#{template_extension}", nil)
10
+ format.to_s
11
+ end
12
+
13
+ context 'when only handler and format are present' do
14
+ let(:template_extension) { 'html.erb' }
15
+
16
+ it { expect(template_format).to eq 'text/html' }
17
+ end
18
+
19
+ context 'when handler, format and version are present' do
20
+ let(:template_extension) { 'json.v1.jbuilder' }
21
+
22
+ it { expect(template_format).to eq 'application/json' }
23
+ end
24
+
25
+ context 'when handler, format and locale are present' do
26
+ let(:template_extension) { 'en.json.jbuilder' }
27
+
28
+ it { expect(template_format).to eq 'application/json' }
29
+ end
30
+
31
+ context 'when handler, format, locale and version are present' do
32
+ let(:template_extension) { 'en.json.v1.jbuilder' }
33
+
34
+ it { expect(template_format).to eq 'application/json' }
35
+ end
36
+
37
+ context 'when handler, format, variant and version are present' do
38
+ let(:template_extension) { 'application.json+tablet.v1.jbuilder' }
39
+
40
+ it { expect(template_format).to eq 'application/json' }
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,76 @@
1
+ require './spec/rails_helper'
2
+
3
+ if ActionPack::VERSION::MAJOR == 7
4
+ describe ActionView::Resolver::PathParser do
5
+ let(:resolver) { ActionView::Resolver::PathParser.new }
6
+
7
+ context '#extract_handler_and_format' do
8
+ subject(:parsed_path) do
9
+ resolver.parse("application.#{template_extension}")
10
+ end
11
+
12
+ context 'when only handler and format are present' do
13
+ let(:template_extension) { 'html.erb' }
14
+
15
+ it do
16
+ expect(parsed_path.details.format).to eq :html
17
+ expect(parsed_path.details.handler).to eq :erb
18
+ end
19
+ end
20
+
21
+ context 'when handler, format and version are present' do
22
+ let(:template_extension) { 'json.v1.builder' }
23
+
24
+ it do
25
+ expect(parsed_path.details.format).to eq :json
26
+ expect(parsed_path.details.handler).to eq :builder
27
+ expect(parsed_path.details.version).to eq :v1
28
+ end
29
+ end
30
+
31
+ context 'when handler, format and locale are present' do
32
+ let(:template_extension) { 'en.json.builder' }
33
+
34
+ it do
35
+ expect(parsed_path.details.locale).to eq :en
36
+ expect(parsed_path.details.format).to eq :json
37
+ expect(parsed_path.details.handler).to eq :builder
38
+ expect(parsed_path.details.version).to eq nil
39
+ end
40
+ end
41
+
42
+ context 'when handler, format, locale and version are present' do
43
+ let(:template_extension) { 'en.json.v1.builder' }
44
+
45
+ it do
46
+ expect(parsed_path.details.format).to eq :json
47
+ expect(parsed_path.details.handler).to eq :builder
48
+ expect(parsed_path.details.version).to eq :v1
49
+ end
50
+ end
51
+
52
+ context 'when handler, format, variant and version are present' do
53
+ let(:template_extension) { 'json+tablet.v1.builder' }
54
+
55
+ it do
56
+ expect(parsed_path.details.variant).to eq :tablet
57
+ expect(parsed_path.details.format).to eq :json
58
+ expect(parsed_path.details.handler).to eq :builder
59
+ expect(parsed_path.details.version).to eq :v1
60
+ end
61
+ end
62
+
63
+ context 'when handler, format, variant, locale and version are present' do
64
+ let(:template_extension) { 'en.json+tablet.v1.builder' }
65
+
66
+ it do
67
+ expect(parsed_path.details.locale).to eq :en
68
+ expect(parsed_path.details.variant).to eq :tablet
69
+ expect(parsed_path.details.format).to eq :json
70
+ expect(parsed_path.details.handler).to eq :builder
71
+ expect(parsed_path.details.version).to eq :v1
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -60,13 +60,13 @@ describe VersionCake::ExtractionStrategy do
60
60
  it "it fails to create a custom strategy for a proc with no parameters" do
61
61
  expect do
62
62
  VersionCake::ExtractionStrategy.lookup(lambda{})
63
- end.to raise_error(Exception)
63
+ end.to raise_error(VersionCake::ExtractionStrategy::InvalidStrategyError)
64
64
  end
65
65
 
66
66
  it "it raises error when no strategy is found" do
67
67
  expect do
68
68
  VersionCake::ExtractionStrategy.lookup(:fake_extraction)
69
- end.to raise_error(Exception)
69
+ end.to raise_error(VersionCake::ExtractionStrategy::InvalidStrategyError)
70
70
  end
71
71
 
72
72
  describe '.list' do
@@ -10,7 +10,7 @@ describe VersionCake::VersionChecker do
10
10
  end
11
11
  let(:version) { }
12
12
  subject do
13
- checker = VersionCake::VersionChecker.new(resource, version)
13
+ checker = VersionCake::VersionChecker.new(version, resource)
14
14
  checker.execute
15
15
  end
16
16
 
@@ -24,12 +24,5 @@ describe VersionCake::VersionedRequest do
24
24
  it { expect(versioned_request.version).to be_nil }
25
25
  it { expect(versioned_request.failed).to be_falsey }
26
26
  end
27
-
28
- context 'with a strategy failure' do
29
- let(:strategies) { [ lambda { raise 'Failed extraction' } ] }
30
-
31
- it { expect(versioned_request.version).to be_nil }
32
- it { expect(versioned_request.failed).to be_truthy }
33
- end
34
27
  end
35
28
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: versioncake
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jim Jones
8
8
  - Ben Willis
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-03-12 00:00:00.000000000 Z
12
+ date: 2022-04-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionpack
@@ -146,9 +146,9 @@ executables:
146
146
  extensions: []
147
147
  extra_rdoc_files: []
148
148
  files:
149
+ - ".github/workflows/ruby.yml"
149
150
  - ".gitignore"
150
151
  - ".rspec"
151
- - ".travis.yml"
152
152
  - Appraisals
153
153
  - CHANGELOG.md
154
154
  - CONTRIBUTING.md
@@ -157,6 +157,7 @@ files:
157
157
  - README.md
158
158
  - RELEASE.md
159
159
  - Rakefile
160
+ - SECURITY.md
160
161
  - bin/versioncake
161
162
  - gemfiles/.bundle/config
162
163
  - gemfiles/rails5.0.gemfile
@@ -165,6 +166,8 @@ files:
165
166
  - gemfiles/rails5.2.gemfile.lock
166
167
  - gemfiles/rails6.0.gemfile
167
168
  - gemfiles/rails6.0.gemfile.lock
169
+ - gemfiles/rails7.0.gemfile
170
+ - gemfiles/rails7.0.gemfile.lock
168
171
  - images/versioncake-logo450x100.png
169
172
  - lib/generators/templates/versioncake.rb
170
173
  - lib/generators/versioncake/install_generator.rb
@@ -175,6 +178,7 @@ files:
175
178
  - lib/versioncake/engine.rb
176
179
  - lib/versioncake/exceptions.rb
177
180
  - lib/versioncake/rack/middleware.rb
181
+ - lib/versioncake/railtie.rb
178
182
  - lib/versioncake/response_strategy/base.rb
179
183
  - lib/versioncake/response_strategy/http_content_type_strategy.rb
180
184
  - lib/versioncake/response_strategy/http_header_strategy.rb
@@ -194,6 +198,9 @@ files:
194
198
  - lib/versioncake/versioned_resource.rb
195
199
  - lib/versioncake/versioned_response_service.rb
196
200
  - lib/versioncake/view_additions.rb
201
+ - lib/versioncake/view_additions_rails5.rb
202
+ - lib/versioncake/view_additions_rails6.rb
203
+ - lib/versioncake/view_additions_rails7.rb
197
204
  - spec/fixtures/partials/_versioned.erb
198
205
  - spec/fixtures/partials/_versioned.v1.erb
199
206
  - spec/fixtures/partials/_versioned.v2.erb
@@ -212,7 +219,9 @@ files:
212
219
  - spec/integration/controller/unversioned_controller_spec.rb
213
220
  - spec/integration/rack/middleware_regression_spec.rb
214
221
  - spec/integration/view/render_spec.rb
215
- - spec/integration/view/view_additions_spec.rb
222
+ - spec/integration/view/view_additions_rails5_spec.rb
223
+ - spec/integration/view/view_additions_rails6_spec.rb
224
+ - spec/integration/view/view_additions_rails7_spec.rb
216
225
  - spec/rails_helper.rb
217
226
  - spec/spec_helper.rb
218
227
  - spec/test_app/Rakefile
@@ -252,7 +261,7 @@ homepage: http://bwillis.github.io/versioncake
252
261
  licenses:
253
262
  - MIT
254
263
  metadata: {}
255
- post_install_message:
264
+ post_install_message:
256
265
  rdoc_options: []
257
266
  require_paths:
258
267
  - lib
@@ -267,8 +276,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
267
276
  - !ruby/object:Gem::Version
268
277
  version: '0'
269
278
  requirements: []
270
- rubygems_version: 3.0.3
271
- signing_key:
279
+ rubygems_version: 3.1.6
280
+ signing_key:
272
281
  specification_version: 4
273
282
  summary: Easily render versions of your rails views.
274
283
  test_files: []
data/.travis.yml DELETED
@@ -1,20 +0,0 @@
1
- language: ruby
2
-
3
- before_install: gem install bundler && bundle update --bundler
4
-
5
- rvm:
6
- - 2.3.3
7
- - 2.4.7
8
- - 2.5.6
9
-
10
- gemfile:
11
- - gemfiles/rails5.0.gemfile
12
- - gemfiles/rails5.2.gemfile
13
- - gemfiles/rails6.0.gemfile
14
-
15
- matrix:
16
- exclude:
17
- - rvm: 2.3.3
18
- gemfile: gemfiles/rails6.0.gemfile
19
- - rvm: 2.4.7
20
- gemfile: gemfiles/rails6.0.gemfile
@@ -1,42 +0,0 @@
1
- require './spec/rails_helper'
2
-
3
- describe ActionView::PathResolver do
4
- let(:resolver) { ActionView::PathResolver.new }
5
-
6
- context '#extract_handler_and_format' do
7
- subject(:template_format) do
8
- _, format = resolver.extract_handler_and_format("application.#{template_extension}", nil)
9
- format.to_s
10
- end
11
-
12
- context 'when only handler and format are present' do
13
- let(:template_extension) { 'html.erb' }
14
-
15
- it { expect(template_format).to eq 'text/html' }
16
- end
17
-
18
- context 'when handler, format and version are present' do
19
- let(:template_extension) { 'json.v1.jbuilder' }
20
-
21
- it { expect(template_format).to eq 'application/json' }
22
- end
23
-
24
- context 'when handler, format and locale are present' do
25
- let(:template_extension) { 'en.json.jbuilder' }
26
-
27
- it { expect(template_format).to eq 'application/json' }
28
- end
29
-
30
- context 'when handler, format, locale and version are present' do
31
- let(:template_extension) { 'en.json.v1.jbuilder' }
32
-
33
- it { expect(template_format).to eq 'application/json' }
34
- end
35
-
36
- context 'when handler, format, variant and version are present' do
37
- let(:template_extension) { 'application.json+tablet.v1.jbuilder' }
38
-
39
- it { expect(template_format).to eq 'application/json' }
40
- end
41
- end
42
- end