versioncake 4.0.1 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
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