action_version_preview 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +118 -0
- data/Rakefile +6 -0
- data/app/helpers/action_version_preview/switcher_helper.rb +7 -0
- data/app/views/action_version_preview/_variant_switcher.html.erb +13 -0
- data/config/routes.rb +2 -0
- data/lib/action_version_preview/controller_methods.rb +51 -0
- data/lib/action_version_preview/engine.rb +14 -0
- data/lib/action_version_preview/version.rb +3 -0
- data/lib/action_version_preview.rb +25 -0
- data/lib/tasks/action_version_preview_tasks.rake +4 -0
- metadata +76 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: e6d263561ee95f415d87df090bb1e0008500048a1a896c96ae2df8ba7690e6b7
|
|
4
|
+
data.tar.gz: fc72870345f275e9e40fe4d75a5bba496467239e577fb524ecfd2e4b9b740fb2
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f8353f9edc0f76a49425d6a5147b814fc6ca82304c465c186f33a94a8c42f61cea4d8120c0f8c5332636f277705e43e3755f1d38b10706137902d356d5c98011
|
|
7
|
+
data.tar.gz: 87099d11c6a79710263dc7c5b8f58a0e91a066279f60a9f4ad8b2c2a011096f1e33b79db414719b61a237e4f755b0c3950dedc77dd48272135b5138a03e7af80
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright TODO: Write your name
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# ActionVersionPreview
|
|
2
|
+
|
|
3
|
+
Preview multiple versions of your UI simultaneously using Rails' built-in view variants. No feature flags, no complex setup. Just create variant templates and visit them via URL.
|
|
4
|
+
|
|
5
|
+
## Why?
|
|
6
|
+
|
|
7
|
+
Feature flag gems like Flipper are designed for toggling features on/off for users, not for showing multiple versions at once. For design iterations and feedback collection, you want to open three browser tabs side by side, each showing a different version, all logged in as the same user.
|
|
8
|
+
|
|
9
|
+
ActionVersionPreview makes this trivial using Rails' native view variants.
|
|
10
|
+
|
|
11
|
+
<img width="2146" height="2014" alt="image" src="https://github.com/user-attachments/assets/c39b1305-ad14-4958-be39-d714d53d1990" />
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Add to your Gemfile:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem "action_version_preview"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Run `bundle install`. That's it. The concern is automatically included in all controllers.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
### 1. Create Variant Templates
|
|
26
|
+
|
|
27
|
+
Rails looks for variant templates using this naming convention:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
app/views/dashboard/show.html.erb # default
|
|
31
|
+
app/views/dashboard/show.html+v2.erb # variant :v2
|
|
32
|
+
app/views/dashboard/show.html+redesign.erb # variant :redesign
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This works for layouts, partials, mailers, and ViewComponent templates too.
|
|
36
|
+
|
|
37
|
+
### 2. Visit the Variant URL
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
/dashboard # renders show.html.erb
|
|
41
|
+
/dashboard?vv=v2 # renders show.html+v2.erb
|
|
42
|
+
/dashboard?vv=true # renders default, but activates the switcher
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 3. Add the Variant Switcher (Optional)
|
|
46
|
+
|
|
47
|
+
Drop the built-in switcher widget in your layout:
|
|
48
|
+
|
|
49
|
+
```erb
|
|
50
|
+
<%= variant_switcher %>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The switcher automatically detects available variants by scanning the view directory for `+variant` template files. It only appears when:
|
|
54
|
+
- The `vv` param is present in the URL (e.g., `?vv=true` or `?vv=v2`)
|
|
55
|
+
- The current action has variant templates available
|
|
56
|
+
- The user can preview variants (dev/test by default)
|
|
57
|
+
|
|
58
|
+
Standard Rails variants (`mobile`, `tablet`, `phone`, `desktop`) are excluded from detection.
|
|
59
|
+
|
|
60
|
+
## Configuration
|
|
61
|
+
|
|
62
|
+
Zero config is the default. But if you need to customize:
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
# config/initializers/action_version_preview.rb
|
|
66
|
+
ActionVersionPreview.configure do |config|
|
|
67
|
+
# Change the URL parameter (default: :vv)
|
|
68
|
+
config.param_name = :v
|
|
69
|
+
|
|
70
|
+
# Control who can preview variants (default: dev/test only)
|
|
71
|
+
# In production, you might want admins only:
|
|
72
|
+
config.access_check = ->(controller) {
|
|
73
|
+
Rails.env.development? ||
|
|
74
|
+
Rails.env.test? ||
|
|
75
|
+
controller.current_user&.admin?
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Helper Methods
|
|
81
|
+
|
|
82
|
+
These are available in controllers and views:
|
|
83
|
+
|
|
84
|
+
| Method | Description |
|
|
85
|
+
|--------|-------------|
|
|
86
|
+
| `current_variant` | Returns the active variant symbol (e.g., `:v2`) or `nil` |
|
|
87
|
+
| `detected_variants` | Returns array of variant names found for current action |
|
|
88
|
+
| `variant_preview_active?` | Returns true if variant preview mode is active |
|
|
89
|
+
| `can_preview_variants?` | Returns true if current user can access variants |
|
|
90
|
+
| `variant_switcher` | Renders the switcher widget |
|
|
91
|
+
|
|
92
|
+
## How It Works
|
|
93
|
+
|
|
94
|
+
Under the hood, ActionVersionPreview sets `request.variant` based on the URL parameter. Rails' template resolver then automatically looks for matching variant templates.
|
|
95
|
+
|
|
96
|
+
When you visit `/dashboard?vv=v2`:
|
|
97
|
+
1. The `before_action` extracts `vv=v2` from params
|
|
98
|
+
2. It sets `request.variant = :v2`
|
|
99
|
+
3. Rails renders `show.html+v2.erb` instead of `show.html.erb`
|
|
100
|
+
4. The switcher detects all `show.html+*.erb` variants in the view directory
|
|
101
|
+
|
|
102
|
+
## Comparison: Feature Flags vs View Variants
|
|
103
|
+
|
|
104
|
+
| Feature Flags (Flipper) | View Variants (This Gem) |
|
|
105
|
+
|------------------------|--------------------------|
|
|
106
|
+
| Toggle features on/off for users | Access all versions simultaneously |
|
|
107
|
+
| One version "live" at a time | Side-by-side comparison in multiple tabs |
|
|
108
|
+
| Percentage rollouts, A/B testing | Design iteration, feedback collection |
|
|
109
|
+
| Requires database/Redis | Zero dependencies |
|
|
110
|
+
|
|
111
|
+
## Requirements
|
|
112
|
+
|
|
113
|
+
- Rails 7.0+ or 8.x
|
|
114
|
+
- Ruby 3.1+
|
|
115
|
+
|
|
116
|
+
## License
|
|
117
|
+
|
|
118
|
+
MIT License. See [MIT-LICENSE](MIT-LICENSE).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<% if variant_preview_active? && detected_variants.any? %>
|
|
2
|
+
<div style="position: fixed; bottom: 1rem; right: 1rem; background: #18181b; color: white; padding: 0.75rem; border-radius: 0.5rem; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); z-index: 9999; font-size: 0.875rem; font-family: system-ui, -apple-system, sans-serif;">
|
|
3
|
+
<span style="color: #a1a1aa; margin-right: 0.5rem;">View:</span>
|
|
4
|
+
<%= link_to "Default",
|
|
5
|
+
url_for(request.params.except(ActionVersionPreview.param_name.to_s, ActionVersionPreview.param_name.to_sym)),
|
|
6
|
+
style: "padding: 0.25rem 0.5rem; border-radius: 0.25rem; text-decoration: none; color: white; #{current_variant.nil? ? 'background: #2563eb;' : ''}" %>
|
|
7
|
+
<% detected_variants.each do |variant| %>
|
|
8
|
+
<%= link_to variant.upcase,
|
|
9
|
+
url_for(request.params.merge(ActionVersionPreview.param_name => variant)),
|
|
10
|
+
style: "padding: 0.25rem 0.5rem; border-radius: 0.25rem; text-decoration: none; color: white; #{current_variant.to_s == variant ? 'background: #2563eb;' : ''}" %>
|
|
11
|
+
<% end %>
|
|
12
|
+
</div>
|
|
13
|
+
<% end %>
|
data/config/routes.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module ActionVersionPreview
|
|
2
|
+
module ControllerMethods
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
# Standard Rails variants that should be excluded from detection
|
|
6
|
+
STANDARD_VARIANTS = %w[mobile tablet phone desktop].freeze
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
before_action :set_view_variant
|
|
10
|
+
helper_method :current_variant, :detected_variants, :can_preview_variants?, :variant_preview_active?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def set_view_variant
|
|
16
|
+
variant = params[ActionVersionPreview.param_name]
|
|
17
|
+
return unless variant.present?
|
|
18
|
+
return unless can_preview_variants?
|
|
19
|
+
|
|
20
|
+
# Set the variant if it's not just a truthy trigger value
|
|
21
|
+
request.variant = variant.to_sym unless variant.to_s == "true"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def current_variant
|
|
25
|
+
request.variant&.first
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns true if the variant preview mode is active (param is present)
|
|
29
|
+
def variant_preview_active?
|
|
30
|
+
params[ActionVersionPreview.param_name].present? && can_preview_variants?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Auto-detect variant files for the current controller action
|
|
34
|
+
def detected_variants
|
|
35
|
+
@detected_variants ||= begin
|
|
36
|
+
view_path = Rails.root.join("app", "views", controller_path)
|
|
37
|
+
pattern = "#{action_name}.html+*.erb"
|
|
38
|
+
|
|
39
|
+
Dir.glob(view_path.join(pattern)).filter_map do |file|
|
|
40
|
+
match = File.basename(file).match(/\.html\+(\w+)\.erb$/)
|
|
41
|
+
variant = match[1] if match
|
|
42
|
+
variant unless STANDARD_VARIANTS.include?(variant)
|
|
43
|
+
end.sort
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def can_preview_variants?
|
|
48
|
+
ActionVersionPreview.access_check.call(self)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "action_version_preview/controller_methods"
|
|
2
|
+
|
|
3
|
+
module ActionVersionPreview
|
|
4
|
+
class Engine < ::Rails::Engine
|
|
5
|
+
isolate_namespace ActionVersionPreview
|
|
6
|
+
|
|
7
|
+
initializer "action_version_preview.controller_methods" do
|
|
8
|
+
ActiveSupport.on_load(:action_controller_base) do
|
|
9
|
+
include ActionVersionPreview::ControllerMethods
|
|
10
|
+
helper ActionVersionPreview::Engine.helpers
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require "action_version_preview/version"
|
|
2
|
+
require "action_version_preview/engine"
|
|
3
|
+
|
|
4
|
+
module ActionVersionPreview
|
|
5
|
+
class << self
|
|
6
|
+
# The URL parameter name used to specify the variant (default: :vv)
|
|
7
|
+
# Example: /dashboard?vv=v2
|
|
8
|
+
attr_accessor :param_name
|
|
9
|
+
|
|
10
|
+
# Proc that determines if the current user can preview variants
|
|
11
|
+
# Default: always true in development/test, always false in production
|
|
12
|
+
# Example: ->(controller) { controller.current_user&.admin? }
|
|
13
|
+
attr_accessor :access_check
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Set sensible defaults
|
|
17
|
+
self.param_name = :vv
|
|
18
|
+
self.access_check = ->(controller) {
|
|
19
|
+
Rails.env.development? || Rails.env.test?
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
def self.configure
|
|
23
|
+
yield self
|
|
24
|
+
end
|
|
25
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: action_version_preview
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Avi Flombaum
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '7.0'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '9.0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '7.0'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '9.0'
|
|
32
|
+
description: A zero-config Rails engine that leverages Rails' view variants to let
|
|
33
|
+
you preview different versions of your UI simultaneously. Perfect for A/B design
|
|
34
|
+
comparisons, UI iterations, and collecting feedback on redesigns.
|
|
35
|
+
email:
|
|
36
|
+
- im@avi.nyc
|
|
37
|
+
executables: []
|
|
38
|
+
extensions: []
|
|
39
|
+
extra_rdoc_files: []
|
|
40
|
+
files:
|
|
41
|
+
- MIT-LICENSE
|
|
42
|
+
- README.md
|
|
43
|
+
- Rakefile
|
|
44
|
+
- app/helpers/action_version_preview/switcher_helper.rb
|
|
45
|
+
- app/views/action_version_preview/_variant_switcher.html.erb
|
|
46
|
+
- config/routes.rb
|
|
47
|
+
- lib/action_version_preview.rb
|
|
48
|
+
- lib/action_version_preview/controller_methods.rb
|
|
49
|
+
- lib/action_version_preview/engine.rb
|
|
50
|
+
- lib/action_version_preview/version.rb
|
|
51
|
+
- lib/tasks/action_version_preview_tasks.rake
|
|
52
|
+
homepage: https://github.com/aviflombaum/action_version_preview
|
|
53
|
+
licenses:
|
|
54
|
+
- MIT
|
|
55
|
+
metadata:
|
|
56
|
+
homepage_uri: https://github.com/aviflombaum/action_version_preview
|
|
57
|
+
source_code_uri: https://github.com/aviflombaum/action_version_preview
|
|
58
|
+
changelog_uri: https://github.com/aviflombaum/action_version_preview/blob/main/CHANGELOG.md
|
|
59
|
+
rdoc_options: []
|
|
60
|
+
require_paths:
|
|
61
|
+
- lib
|
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: 3.1.0
|
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
|
+
requirements:
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: '0'
|
|
72
|
+
requirements: []
|
|
73
|
+
rubygems_version: 3.6.9
|
|
74
|
+
specification_version: 4
|
|
75
|
+
summary: Preview multiple view variants side-by-side using Rails' built-in view variants.
|
|
76
|
+
test_files: []
|