shimmer 0.0.1 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.editorconfig +12 -0
- data/.eslintrc.js +25 -0
- data/.prettierrc +3 -0
- data/.ruby-version +1 -0
- data/.solargraph.yml +4 -0
- data/.vscode/settings.json +3 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +87 -0
- data/README.md +172 -14
- data/lib/shimmer/controllers/files_controller.rb +15 -0
- data/lib/shimmer/controllers/sitemaps_controller.rb +10 -0
- data/lib/shimmer/jobs/sitemap_job.rb +10 -0
- data/lib/shimmer/middlewares/cloudflare.rb +17 -0
- data/lib/shimmer/railtie.rb +8 -0
- data/lib/shimmer/tasks/db.rake +42 -0
- data/lib/shimmer/tasks/lint.rake +9 -0
- data/lib/shimmer/utils/file_helper.rb +27 -0
- data/lib/shimmer/utils/file_proxy.rb +65 -0
- data/lib/shimmer/utils/localizable.rb +58 -0
- data/lib/shimmer/utils/remote_navigation.rb +112 -0
- data/lib/shimmer/utils/sitemap_adapter.rb +10 -0
- data/lib/shimmer/version.rb +1 -1
- data/lib/shimmer.rb +5 -0
- data/package.json +51 -0
- data/rollup.config.js +26 -0
- data/src/controllers/remote-navigation.ts +9 -0
- data/src/index.ts +32 -0
- data/src/locale.ts +3 -0
- data/src/modal.ts +73 -0
- data/src/popover.ts +89 -0
- data/src/serviceworker.ts +7 -0
- data/src/touch.ts +20 -0
- data/src/util.ts +31 -0
- data/tsconfig.json +28 -0
- data/typings.d.ts +1 -0
- data/yarn.lock +1055 -0
- metadata +35 -4
- data/shimmer.gemspec +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4c44b134bb61f78f82008a1907c738d358b48147ad6518211b7ffcef9fa4aab
|
4
|
+
data.tar.gz: 4b7eb93d01ed55818f0845498e3b33a531352dde7d72b491addded5a77f81ce5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49e8e4866c5d8bafc59d5ccbcac324afd214d2e83f31845a3ff207400059f25061629261ab494fbaf2d2ce1f8be575f978938f11b347d7e751606724e48b666d
|
7
|
+
data.tar.gz: 1e0892571dc244cd238be164c61d4c1b5480e38a994dc266c1d8e8652102a89efcf4ffa8686dd7d8d439dad8aad5080b2faadaa6b3b426f933567a36e16e7b7e
|
data/.editorconfig
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
; EditorConfig is awesome: http://EditorConfig.org
|
2
|
+
|
3
|
+
; top-most EditorConfig file
|
4
|
+
root = true
|
5
|
+
|
6
|
+
; Unix-style newlines with a newline ending every file
|
7
|
+
[*]
|
8
|
+
indent_style = spaces
|
9
|
+
end_of_line = lf
|
10
|
+
insert_final_newline = true
|
11
|
+
trim_trailing_whitespace = true
|
12
|
+
indent_size = 2
|
data/.eslintrc.js
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module.exports = {
|
2
|
+
env: {
|
3
|
+
browser: true,
|
4
|
+
es6: true,
|
5
|
+
},
|
6
|
+
parser: "@typescript-eslint/parser",
|
7
|
+
extends: [
|
8
|
+
"plugin:@typescript-eslint/recommended",
|
9
|
+
"plugin:prettier/recommended",
|
10
|
+
],
|
11
|
+
parserOptions: {
|
12
|
+
ecmaVersion: 2018,
|
13
|
+
sourceType: "module",
|
14
|
+
},
|
15
|
+
plugins: ["@typescript-eslint"],
|
16
|
+
rules: {
|
17
|
+
"no-console": 2,
|
18
|
+
"@typescript-eslint/explicit-function-return-type": [
|
19
|
+
"error",
|
20
|
+
{
|
21
|
+
allowExpressions: true,
|
22
|
+
},
|
23
|
+
],
|
24
|
+
},
|
25
|
+
};
|
data/.prettierrc
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.3
|
data/.solargraph.yml
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
shimmer (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.2)
|
10
|
+
backport (1.2.0)
|
11
|
+
benchmark (0.2.0)
|
12
|
+
diff-lcs (1.4.4)
|
13
|
+
e2mmap (0.1.0)
|
14
|
+
jaro_winkler (1.5.4)
|
15
|
+
kramdown (2.3.1)
|
16
|
+
rexml
|
17
|
+
kramdown-parser-gfm (1.1.0)
|
18
|
+
kramdown (~> 2.0)
|
19
|
+
nokogiri (1.12.5-x86_64-darwin)
|
20
|
+
racc (~> 1.4)
|
21
|
+
nokogiri (1.12.5-x86_64-linux)
|
22
|
+
racc (~> 1.4)
|
23
|
+
parallel (1.21.0)
|
24
|
+
parser (3.0.3.2)
|
25
|
+
ast (~> 2.4.1)
|
26
|
+
racc (1.6.0)
|
27
|
+
rainbow (3.0.0)
|
28
|
+
rake (13.0.6)
|
29
|
+
regexp_parser (2.2.0)
|
30
|
+
reverse_markdown (2.1.1)
|
31
|
+
nokogiri
|
32
|
+
rexml (3.2.5)
|
33
|
+
rubocop (1.23.0)
|
34
|
+
parallel (~> 1.10)
|
35
|
+
parser (>= 3.0.0.0)
|
36
|
+
rainbow (>= 2.2.2, < 4.0)
|
37
|
+
regexp_parser (>= 1.8, < 3.0)
|
38
|
+
rexml
|
39
|
+
rubocop-ast (>= 1.12.0, < 2.0)
|
40
|
+
ruby-progressbar (~> 1.7)
|
41
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
42
|
+
rubocop-ast (1.15.0)
|
43
|
+
parser (>= 3.0.1.1)
|
44
|
+
rubocop-performance (1.12.0)
|
45
|
+
rubocop (>= 1.7.0, < 2.0)
|
46
|
+
rubocop-ast (>= 0.4.0)
|
47
|
+
ruby-progressbar (1.11.0)
|
48
|
+
solargraph (0.44.2)
|
49
|
+
backport (~> 1.2)
|
50
|
+
benchmark
|
51
|
+
bundler (>= 1.17.2)
|
52
|
+
diff-lcs (~> 1.4)
|
53
|
+
e2mmap
|
54
|
+
jaro_winkler (~> 1.5)
|
55
|
+
kramdown (~> 2.3)
|
56
|
+
kramdown-parser-gfm (~> 1.1)
|
57
|
+
parser (~> 3.0)
|
58
|
+
reverse_markdown (>= 1.0.5, < 3)
|
59
|
+
rubocop (>= 0.52)
|
60
|
+
thor (~> 1.0)
|
61
|
+
tilt (~> 2.0)
|
62
|
+
yard (~> 0.9, >= 0.9.24)
|
63
|
+
solargraph-standardrb (0.0.4)
|
64
|
+
solargraph (>= 0.39.1)
|
65
|
+
standard (>= 0.4.1)
|
66
|
+
standard (1.5.0)
|
67
|
+
rubocop (= 1.23.0)
|
68
|
+
rubocop-performance (= 1.12.0)
|
69
|
+
thor (1.1.0)
|
70
|
+
tilt (2.0.10)
|
71
|
+
unicode-display_width (2.1.0)
|
72
|
+
webrick (1.7.0)
|
73
|
+
yard (0.9.27)
|
74
|
+
webrick (~> 1.7.0)
|
75
|
+
|
76
|
+
PLATFORMS
|
77
|
+
x86_64-darwin-21
|
78
|
+
x86_64-linux
|
79
|
+
|
80
|
+
DEPENDENCIES
|
81
|
+
rake (~> 13.0)
|
82
|
+
shimmer!
|
83
|
+
solargraph-standardrb
|
84
|
+
standard
|
85
|
+
|
86
|
+
BUNDLED WITH
|
87
|
+
2.2.32
|
data/README.md
CHANGED
@@ -1,38 +1,196 @@
|
|
1
|
-
# Shimmer
|
1
|
+
# Shimmer - Because Ruby could be more shiny!
|
2
2
|
|
3
|
-
|
3
|
+
Shimmer is a collection of Rails extensions that bring advanced UI features into your app and make your life easier as a developer.
|
4
4
|
|
5
|
-
|
5
|
+
## Features
|
6
|
+
|
7
|
+
### Static File Serving
|
8
|
+
|
9
|
+
`ActiveStorage` is great, but serving of files, especially behind a CDN, can be complicated to get right. This can be fixed easily:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
# use an image tag
|
13
|
+
image_tag user.avatar, width: 300
|
14
|
+
```
|
15
|
+
|
16
|
+
This extension overrides `image_tag` and also supplies a matching `image_file_url` that automatically resizes your image and creates a static, cacheable url.
|
17
|
+
|
18
|
+
### Modals
|
19
|
+
|
20
|
+
Modals are the designer's best friend, but developers usually hate them for their complexity. Fear no more: Shimmer has you covered.
|
21
|
+
|
22
|
+
```slim
|
23
|
+
a href=modal_path(new_post_path) Create a new Post
|
24
|
+
```
|
25
|
+
|
26
|
+
This will open a modal on click and then asynchronously request the modal content from the controller. Modals can also be controlled via JavaScript via the global `ui` variable:
|
27
|
+
|
28
|
+
```js
|
29
|
+
ui.modal.open({ url: "/posts/new" });
|
30
|
+
ui.modal.close();
|
31
|
+
```
|
32
|
+
|
33
|
+
### Popovers
|
34
|
+
|
35
|
+
When modals are annoying to implement, popovers are even worse. Thankfully, Shimmer comes to the rescue:
|
36
|
+
|
37
|
+
```slim
|
38
|
+
a href=popover_path(new_post_path, placement: :left)
|
39
|
+
```
|
40
|
+
|
41
|
+
This will request `new_post_path` and display it left of the anchor thanks to PopperJS.
|
42
|
+
|
43
|
+
### Remote Navigation
|
44
|
+
|
45
|
+
Remote navigation takes Hotwire to the next level with built-in navigation actions, integrated with modals and popovers.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
def create
|
49
|
+
@post = current_user.posts.create! post_params
|
50
|
+
ui.navigate_to @post
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
This will automatically close the current modal or popover and navigate via Turbo Drive to the post's url - no redirects necessary.
|
55
|
+
|
56
|
+
The `ui` helper comes with several built-in functions:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
# run any kind of javascript upon request completion
|
60
|
+
ui.run_javascript("alert('hello world')")
|
61
|
+
|
62
|
+
# open or replace a modal's content (dependent on its ID)
|
63
|
+
ui.open_modal(new_post_path, size: :small)
|
64
|
+
|
65
|
+
# close an open modal
|
66
|
+
ui.close_modal
|
67
|
+
|
68
|
+
# same methods also available for popovers
|
69
|
+
ui.open_popover(new_post_path, selector: "#user-profile", placement: :left)
|
70
|
+
ui.close_popover
|
71
|
+
|
72
|
+
# navigate via Turbo Drive
|
73
|
+
ui.navigate_to(@post)
|
74
|
+
|
75
|
+
# manipulate the page's content
|
76
|
+
ui.append(@post, with: "comments/comment", comment: @comment)
|
77
|
+
ui.prepend("#user-profile", with: "users/extra")
|
78
|
+
ui.replace(@post)
|
79
|
+
ui.remove(@post)
|
80
|
+
```
|
81
|
+
|
82
|
+
### Sitemaps
|
83
|
+
|
84
|
+
Want to implement sitemaps, but the ephemeral filesystem of Heroku hates you? Here's a simple way to upload sitemaps:
|
85
|
+
|
86
|
+
- install the sitemap gem and configure the `sitemap.rb` as usual
|
87
|
+
- use the shimmer adapter to automatically upload your sitemap to your configured ActiveStorage adapter
|
88
|
+
- use the shimmer controller to display the sitemap in your app
|
89
|
+
- (optional) tell sidekiq scheduler to regularly update your sitemap
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
# sitemap.rb
|
93
|
+
SitemapGenerator::Sitemap.adapter = Shimmer::SitemapAdapter.new
|
94
|
+
|
95
|
+
# routes.rb
|
96
|
+
get "sitemaps/*path", to: "shimmer/sitemaps#show"
|
97
|
+
|
98
|
+
# sidekiq.yml
|
99
|
+
:schedule:
|
100
|
+
sitemap:
|
101
|
+
cron: '0 0 12 * * * Europe/Berlin' # every day at 16:00, Berlin time
|
102
|
+
class: Shimmer::SitemapJob
|
103
|
+
```
|
104
|
+
|
105
|
+
### Cloudflare Support
|
106
|
+
|
107
|
+
As you might have noticed, Cloudflare SSL will cause some issues with your Rails app if you're not using SSL strict mode (https://github.com/rails/rails/issues/22965). If you can't switch to strict mode, go for the standard flexible mode instead and add this middleware to your stack:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
# application.rb
|
111
|
+
config.middleware.use Shimmer::CloudflareProxy
|
112
|
+
```
|
113
|
+
|
114
|
+
### Heroku Database Helpers
|
115
|
+
|
116
|
+
Can't reproduce an issue with your local test data and just want the production or staging data on your development machine? Here you go:
|
117
|
+
|
118
|
+
```bash
|
119
|
+
rails db:pull
|
120
|
+
```
|
121
|
+
|
122
|
+
This will drop your local database and pull in the database of your connected Heroku app (make sure you executed `heroku git:remote -a your_app` before to have the git remote). But what about assets you might ask? Easy - assets are pulled from S3 as well via the AWS CLI automatically (make sure your environment variables in Heroku are correctly named as `AWS_REGION`, `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`) and the database is updated to use your local filesystem instead.
|
123
|
+
|
124
|
+
If you don't want the asset support, you can also only pull the database or only the assets:
|
125
|
+
|
126
|
+
```bash
|
127
|
+
rails db:pull_data
|
128
|
+
rails db:pull_assets
|
129
|
+
```
|
130
|
+
|
131
|
+
### Localizable Routes with Browser Locale Support
|
132
|
+
|
133
|
+
To localize a page via urls, this will help you tremendously.
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
# routes.rb
|
137
|
+
Rails.application.routes.draw do
|
138
|
+
scope "/(:locale)", locale: /#{I18n.available_locales.join("|")}/ do
|
139
|
+
get "login", to: "sessions#new"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
From now on you can prefix your routes with a locale like `/de/login` or `/en/login` and `I18n.locale` will automatically be set. If there is no locale in the url (it's optional), this will automatically use the browser's locale.
|
145
|
+
|
146
|
+
You want to redirect from unlocalized paths? Add a before action to your controller:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
before_action :check_locale
|
150
|
+
```
|
151
|
+
|
152
|
+
Trying to figure out which key a certain translation on the page has? Append `?debug` to the url and `I18n.debug?` will be set - which leads to keys being printed on the page.
|
6
153
|
|
7
154
|
## Installation
|
8
155
|
|
9
156
|
Add this line to your application's Gemfile:
|
10
157
|
|
11
158
|
```ruby
|
12
|
-
gem
|
159
|
+
gem "shimmer"
|
13
160
|
```
|
14
161
|
|
15
162
|
And then execute:
|
16
163
|
|
17
|
-
|
164
|
+
```bash
|
165
|
+
$ bundle install
|
166
|
+
```
|
18
167
|
|
19
|
-
|
168
|
+
Add some configuration to your project:
|
20
169
|
|
21
|
-
|
170
|
+
```ruby
|
171
|
+
# routes.rb
|
22
172
|
|
23
|
-
|
173
|
+
resources :files, only: :show, controller: "shimmer/files"
|
24
174
|
|
25
|
-
|
175
|
+
# application_controller.rb
|
176
|
+
class ApplicationController < ActionController::Base
|
177
|
+
include Shimmer::Localizable
|
178
|
+
include Shimmer::RemoteNavigation
|
179
|
+
end
|
180
|
+
```
|
26
181
|
|
27
|
-
|
182
|
+
```ts
|
183
|
+
// application.ts
|
28
184
|
|
29
|
-
|
185
|
+
import { start } from "@nerdgeschoss/shimmer";
|
186
|
+
import { application } from "controllers/application";
|
30
187
|
|
31
|
-
|
188
|
+
start({ application });
|
189
|
+
```
|
32
190
|
|
33
191
|
## Contributing
|
34
192
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
193
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/nerdgeschoss/shimmer. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/nerdgeschoss/shimmer/blob/master/CODE_OF_CONDUCT.md).
|
36
194
|
|
37
195
|
## License
|
38
196
|
|
@@ -40,4 +198,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
40
198
|
|
41
199
|
## Code of Conduct
|
42
200
|
|
43
|
-
Everyone interacting in the Shimmer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
201
|
+
Everyone interacting in the Shimmer project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/nerdgeschoss/shimmer/blob/master/CODE_OF_CONDUCT.md).
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shimmer
|
4
|
+
class FilesController < ActionController::Base
|
5
|
+
def show
|
6
|
+
expires_in 1.year, public: true
|
7
|
+
request.session_options[:skip] = true # prevents a session cookie from being set (would prevent caching on CDNs)
|
8
|
+
proxy = FileProxy.restore(params.require(:id))
|
9
|
+
send_data proxy.file,
|
10
|
+
filename: proxy.filename.to_s,
|
11
|
+
type: proxy.content_type,
|
12
|
+
disposition: "inline"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# https://github.com/rails/rails/issues/22965
|
4
|
+
module Shimmer
|
5
|
+
class CloudflareProxy
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
return @app.call(env) unless env["HTTP_CF_VISITOR"]
|
12
|
+
|
13
|
+
env["HTTP_X_FORWARDED_PROTO"] = JSON.parse(env["HTTP_CF_VISITOR"])["scheme"]
|
14
|
+
@app.call(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :db do
|
4
|
+
desc "Downloads the app database from heroku to local db"
|
5
|
+
task pull_data: :environment do
|
6
|
+
config = ActiveRecord::Base.connection_db_config.config
|
7
|
+
# binding.pry
|
8
|
+
ENV["DISABLE_DATABASE_ENVIRONMENT_CHECK"] = "1"
|
9
|
+
Rake::Task["db:drop"].invoke
|
10
|
+
ENV["PGUSER"] = config["username"]
|
11
|
+
ENV["PGHOST"] = config["host"]
|
12
|
+
ENV["PGPORT"] = config["port"].to_s
|
13
|
+
sh "heroku pg:pull DATABASE_URL #{config["database"]} --app thefetishtraveller"
|
14
|
+
sh "rails db:environment:set"
|
15
|
+
sh "RAILS_ENV=test rails db:create"
|
16
|
+
end
|
17
|
+
|
18
|
+
task pull_assets: :environment do
|
19
|
+
config = JSON.parse(`heroku config --json`)
|
20
|
+
ENV["AWS_DEFAULT_REGION"] = config.fetch("AWS_REGION")
|
21
|
+
bucket = config.fetch("AWS_BUCKET")
|
22
|
+
ENV["AWS_ACCESS_KEY_ID"] = config.fetch("AWS_ACCESS_KEY_ID")
|
23
|
+
ENV["AWS_SECRET_ACCESS_KEY"] = config.fetch("AWS_SECRET_ACCESS_KEY")
|
24
|
+
storage_folder = Rails.root.join("storage")
|
25
|
+
download_folder = storage_folder.join("downloads")
|
26
|
+
FileUtils.mkdir_p download_folder
|
27
|
+
sh "aws s3 sync s3://#{bucket} #{storage_folder}/downloads"
|
28
|
+
download_folder.each_child do |file|
|
29
|
+
next if file.directory?
|
30
|
+
|
31
|
+
new_path = storage_folder.join file.basename.to_s.then { |e| [e[0..1], e[2..3], e] }.join("/")
|
32
|
+
FileUtils.mkdir_p(new_path.dirname)
|
33
|
+
FileUtils.cp(file, new_path)
|
34
|
+
end
|
35
|
+
# purge variants
|
36
|
+
ActiveStorage::VariantRecord.delete_all
|
37
|
+
ActiveStorage::Blob.update_all(service_name: :local)
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "Download all app data, including assets"
|
41
|
+
task pull: [:pull_data, :pull_assets]
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shimmer
|
4
|
+
module FileHelper
|
5
|
+
def image_tag(source, **options)
|
6
|
+
return nil if source.blank?
|
7
|
+
|
8
|
+
if source.is_a?(ActiveStorage::Variant) || source.is_a?(ActiveStorage::Attached) || source.is_a?(ActiveStorage::Attachment) || source.is_a?(ActionText::Attachment)
|
9
|
+
attachment = source
|
10
|
+
width = options[:width]
|
11
|
+
height = options[:height]
|
12
|
+
source = image_file_url(source, width: width, height: height)
|
13
|
+
options[:loading] = :lazy
|
14
|
+
options[:srcset] = "#{source} 1x, #{image_file_url(attachment, width: width.to_i * 2, height: height ? height.to_i * 2 : nil)} 2x" if options[:width].present?
|
15
|
+
end
|
16
|
+
super source, options
|
17
|
+
end
|
18
|
+
|
19
|
+
def image_file_url(source, width: nil, height: nil)
|
20
|
+
return if source.blank?
|
21
|
+
return source if source.is_a?(String)
|
22
|
+
|
23
|
+
blob = source.try(:blob) || source
|
24
|
+
Shimmer::FileProxy.new(blob_id: blob.id, width: width, height: height).url
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shimmer
|
4
|
+
class FileProxy
|
5
|
+
attr_reader :blob_id, :resize
|
6
|
+
|
7
|
+
delegate :message_verifier, to: :class
|
8
|
+
delegate :content_type, :filename, to: :blob
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def restore(id)
|
12
|
+
blob_id, resize = message_verifier.verified(id)
|
13
|
+
new blob_id: blob_id, resize: resize
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(blob_id:, resize: nil, width: nil, height: nil)
|
18
|
+
@blob_id = blob_id
|
19
|
+
if !resize && width
|
20
|
+
resize = if height
|
21
|
+
"#{width}x#{height}>"
|
22
|
+
else
|
23
|
+
"#{width}x"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
@resize = resize
|
27
|
+
end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
def message_verifier
|
31
|
+
@message_verifier ||= ApplicationRecord.signed_id_verifier
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def path
|
36
|
+
Rails.application.routes.url_helpers.file_path(id, locale: nil)
|
37
|
+
end
|
38
|
+
|
39
|
+
def url(protocol: Rails.env.production? ? :https : :http)
|
40
|
+
Rails.application.routes.url_helpers.file_url(id, locale: nil, protocol: protocol)
|
41
|
+
end
|
42
|
+
|
43
|
+
def blob
|
44
|
+
@blob ||= ActiveStorage::Blob.find(blob_id)
|
45
|
+
end
|
46
|
+
|
47
|
+
def resizeable
|
48
|
+
resize.present? && !blob.content_type.include?("svg")
|
49
|
+
end
|
50
|
+
|
51
|
+
def variant
|
52
|
+
@variantvariant ||= resizeable ? blob.representation(resize: resize).processed : blob
|
53
|
+
end
|
54
|
+
|
55
|
+
def file
|
56
|
+
@file ||= blob.service.download(variant.key)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def id
|
62
|
+
@id ||= message_verifier.generate([blob_id, resize])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shimmer
|
4
|
+
module Localizable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
before_action :set_locale
|
9
|
+
|
10
|
+
def default_url_options(options = {})
|
11
|
+
options = {locale: I18n.locale}.merge options
|
12
|
+
options[:debug] = true if I18n.debug?
|
13
|
+
options
|
14
|
+
end
|
15
|
+
|
16
|
+
def request_locale
|
17
|
+
request.env["HTTP_ACCEPT_LANGUAGE"].to_s[0..1].downcase.presence&.then { |e| e if I18n.available_locales.include?(e.to_sym) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_locale
|
21
|
+
I18n.locale = url_locale || request_locale || I18n.default_locale
|
22
|
+
I18n.debug = params.key?(:debug)
|
23
|
+
end
|
24
|
+
|
25
|
+
def check_locale
|
26
|
+
redirect_to url_for(locale: I18n.locale) if params[:locale].blank? && request.get? && request.format.html?
|
27
|
+
end
|
28
|
+
|
29
|
+
def url_locale
|
30
|
+
params[:locale]
|
31
|
+
end
|
32
|
+
|
33
|
+
I18n.class_eval do
|
34
|
+
thread_mattr_accessor :debug
|
35
|
+
|
36
|
+
class << self
|
37
|
+
alias_method :old_translate, :translate
|
38
|
+
def translate(key, options = {})
|
39
|
+
untranslated_scopes = ["number", "transliterate", "date", "datetime", "errors", "helpers", "support", "time", "faker"].map { |k| "#{k}." }
|
40
|
+
key = key.to_s.downcase
|
41
|
+
untranslated = untranslated_scopes.any? { |e| key.include? e }
|
42
|
+
key_name = [options[:scope], key].flatten.compact.join(".")
|
43
|
+
option_names = options.except(:count, :default, :raise, :scope).map { |k, v| "#{k}=#{v}" }.join(", ")
|
44
|
+
return "#{key_name} #{option_names}" if I18n.debug && !untranslated
|
45
|
+
|
46
|
+
options.reverse_merge!(default: old_translate(key, **options.merge(locale: :de))) if untranslated
|
47
|
+
old_translate(key, **options)
|
48
|
+
end
|
49
|
+
alias_method :t, :translate
|
50
|
+
|
51
|
+
def debug?
|
52
|
+
debug
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|