parklife-rails 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +29 -0
- data/app/controllers/parklife/rails/active_storage/blobs_controller.rb +27 -0
- data/config/routes.rb +22 -0
- data/lib/active_storage/service/parklife_service.rb +19 -0
- data/lib/parklife/rails/activestorage.rb +30 -0
- data/lib/parklife/rails/build_integration.rb +73 -0
- data/lib/parklife/rails/config_refinements.rb +20 -0
- data/lib/parklife/rails/route_set_refinements.rb +10 -0
- data/lib/parklife/rails/version.rb +1 -1
- data/lib/parklife/rails.rb +6 -67
- data/lib/parklife-rails/activestorage.rb +2 -0
- metadata +11 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61520daa959509f52822c34d3b946dca45f285fbdc1389a628389440f7d32c1e
|
4
|
+
data.tar.gz: 75df6c7770beb136684d4c312faf97b78c0aaf67705c4fa67785c600a4b35122
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ead1d16e2162277d7d1d6900af837fbf4db4ac692d5be1515bb4e43f59566b8006bc0dd9be4710fdf5784399d10925e58c88fe79a8de308541b5ee6ffbd3dee
|
7
|
+
data.tar.gz: f56206e1a280ff942ad0014e24fca6c6434ca4dc73d4fdf4ec3e36061475e63ffed7ed16aa3e06a34a0bdacca905d71e1b304e8d84120dc36f620cebc802c686
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -8,6 +8,35 @@ Add the gem to your application's `Gemfile`:
|
|
8
8
|
gem 'parklife-rails'
|
9
9
|
```
|
10
10
|
|
11
|
+
## ActiveStorage integration
|
12
|
+
|
13
|
+
Parklife's ActiveStorage integration allows you to use ActiveStorage as normal in development, then during a Parklife build any encountered attachments are collected and copied to the build directory so they can be served alongside the rest of your static files. This is achieved via a Rails Engine and custom ActiveStorage DiskService which work together to tweak ActiveStorage URLs so they're suitable for a static web server.
|
14
|
+
|
15
|
+
Enable the engine at the bottom of `config/application.rb`:
|
16
|
+
|
17
|
+
> [!NOTE]
|
18
|
+
> This must be done before the app boots so can't be in an initializer.
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
require 'parklife-rails/activestorage'
|
22
|
+
```
|
23
|
+
|
24
|
+
Then switch to Parklife's ActiveStorage service in `config/storage.yml`:
|
25
|
+
|
26
|
+
```yml
|
27
|
+
local:
|
28
|
+
service: Parklife
|
29
|
+
root: <%= Rails.root.join("storage") %>
|
30
|
+
```
|
31
|
+
|
32
|
+
Finally anywhere an attachment is referenced make sure to use the `processed` URL:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
image_tag(
|
36
|
+
blog_post.hero_image.representation(:medium).processed.url
|
37
|
+
)
|
38
|
+
```
|
39
|
+
|
11
40
|
## Contributing
|
12
41
|
|
13
42
|
Bug reports and pull requests are welcome on GitHub at <https://github.com/benpickles/parklife-rails>. 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/benpickles/parklife-rails/blob/main/CODE_OF_CONDUCT.md).
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Parklife
|
3
|
+
module Rails
|
4
|
+
module ActiveStorage
|
5
|
+
class BlobsController < ActionController::Base
|
6
|
+
include ::ActiveStorage::FileServer
|
7
|
+
|
8
|
+
def show
|
9
|
+
blob = ::ActiveStorage::Blob.find_by!(key: params[:key])
|
10
|
+
|
11
|
+
serve_file(
|
12
|
+
named_disk_service(blob.service_name).path_for(blob.key),
|
13
|
+
content_type: blob.content_type,
|
14
|
+
disposition: :inline,
|
15
|
+
)
|
16
|
+
rescue Errno::ENOENT
|
17
|
+
head :not_found
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def named_disk_service(name)
|
22
|
+
::ActiveStorage::Blob.services.fetch(name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
Rails.application.routes.draw do
|
3
|
+
scope Parklife::Rails::ActiveStorage.routes_prefix do
|
4
|
+
get 'blobs/:key/*filename',
|
5
|
+
to: 'parklife/rails/active_storage/blobs#show',
|
6
|
+
as: :parklife_blob_service
|
7
|
+
end
|
8
|
+
|
9
|
+
direct :parklife_blob do |blob, options|
|
10
|
+
route_for(
|
11
|
+
:parklife_blob_service,
|
12
|
+
blob.key,
|
13
|
+
blob.filename,
|
14
|
+
{ only_path: true }.merge(options),
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
resolve('ActiveStorage::Attachment') { |attachment, options| route_for(:parklife_blob, attachment.blob, options) }
|
19
|
+
resolve('ActiveStorage::Blob') { |blob, options| route_for(:parklife_blob, blob, options) }
|
20
|
+
resolve('ActiveStorage::Preview') { |preview, options| route_for(:parklife_blob, preview.blob, options) }
|
21
|
+
resolve('ActiveStorage::VariantWithRecord') { |variant, options| route_for(:parklife_blob, variant.blob, options) }
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_storage/service/disk_service'
|
3
|
+
|
4
|
+
module ActiveStorage
|
5
|
+
class Service::ParklifeService < Service::DiskService
|
6
|
+
def url(key, **options)
|
7
|
+
super.tap do |url|
|
8
|
+
if Parklife::Rails::ActiveStorage.collect_assets
|
9
|
+
Parklife::Rails::ActiveStorage.collect_asset(self, key, url)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def generate_url(key, expires_in:, filename:, content_type:, disposition:) # rubocop:disable Lint/UnusedMethodArgument
|
16
|
+
url_helpers.parklife_blob_service_path(key: key, filename: filename)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Parklife
|
3
|
+
module Rails
|
4
|
+
module ActiveStorage
|
5
|
+
Asset = Struct.new(:service, :key, :url) do
|
6
|
+
def blob_path
|
7
|
+
service.path_for(key)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Engine < ::Rails::Engine
|
12
|
+
isolate_namespace Parklife::Rails::ActiveStorage
|
13
|
+
|
14
|
+
initializer 'parklife.app_integration' do |app|
|
15
|
+
# Disable the standard ActiveStorage routes that will otherwise
|
16
|
+
# prevent a blob being served by Parklife's controller.
|
17
|
+
app.config.active_storage.draw_routes = false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
mattr_accessor :collect_assets, default: false
|
22
|
+
mattr_accessor :collected_assets, default: {}
|
23
|
+
mattr_accessor :routes_prefix, default: 'parklife'
|
24
|
+
|
25
|
+
def self.collect_asset(service, key, url)
|
26
|
+
collected_assets[key] ||= Asset.new(service, key, url)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'fileutils'
|
3
|
+
require 'parklife'
|
4
|
+
require 'rails'
|
5
|
+
require_relative 'config_refinements'
|
6
|
+
require_relative 'route_set_refinements'
|
7
|
+
|
8
|
+
module Parklife
|
9
|
+
module Rails
|
10
|
+
class BuildIntegration < ::Rails::Railtie
|
11
|
+
initializer 'parklife.build_integration' do |app|
|
12
|
+
# The offending middleware is included in Rails (6+) development mode and
|
13
|
+
# rejects a request with a 403 response if its host isn't present in the
|
14
|
+
# allowlist (a security feature). This prevents Parklife from working in
|
15
|
+
# a Rails app out of the box unless you manually add the expected
|
16
|
+
# Parklife base to the hosts allowlist or set it to nil to disable it -
|
17
|
+
# both of which aren't great because they disable the security feature
|
18
|
+
# whenever the development server is booted.
|
19
|
+
#
|
20
|
+
# https://guides.rubyonrails.org/configuring.html#actiondispatch-hostauthorization
|
21
|
+
#
|
22
|
+
# However it's safe to remove the middleware at this point because it
|
23
|
+
# won't be executed in the normal Rails development flow, only via a
|
24
|
+
# Parkfile when parklife/rails is required.
|
25
|
+
if defined?(ActionDispatch::HostAuthorization)
|
26
|
+
app.middleware.delete(ActionDispatch::HostAuthorization)
|
27
|
+
end
|
28
|
+
|
29
|
+
Parklife.application.config.app = app
|
30
|
+
|
31
|
+
# This ensures next tweak is compatibile with Rails 8+ lazy routes.
|
32
|
+
Parklife.application.routes.extend(RouteSetRefinements)
|
33
|
+
|
34
|
+
# Allow use of the Rails application's route helpers when defining
|
35
|
+
# Parklife routes in the block form.
|
36
|
+
Parklife.application.routes.singleton_class.include(app.routes.url_helpers)
|
37
|
+
|
38
|
+
Parklife.application.config.extend(ConfigRefinements)
|
39
|
+
end
|
40
|
+
|
41
|
+
config.after_initialize do |app|
|
42
|
+
# Read the Rails app's URL config and apply it to Parklife's so that the
|
43
|
+
# Rails config can be used as the single source of truth.
|
44
|
+
host, port, protocol = app.default_url_options.values_at(:host, :port, :protocol)
|
45
|
+
protocol = 'https' if app.config.force_ssl
|
46
|
+
path = ActionController::Base.relative_url_root
|
47
|
+
|
48
|
+
Parklife.application.config.base.scheme = protocol if protocol
|
49
|
+
Parklife.application.config.base.host = host if host
|
50
|
+
Parklife.application.config.base.port = port if port
|
51
|
+
Parklife.application.config.base.path = path if path
|
52
|
+
|
53
|
+
# If the host Rails app includes Parklife's ActiveStorage integration
|
54
|
+
# then automatically collect attachments encountered during a build and
|
55
|
+
# copy them to the build directory.
|
56
|
+
if defined?(Parklife::Rails::ActiveStorage)
|
57
|
+
Parklife.application.before_build do
|
58
|
+
ActiveStorage.collected_assets.clear
|
59
|
+
ActiveStorage.collect_assets = true
|
60
|
+
end
|
61
|
+
|
62
|
+
Parklife.application.after_build do
|
63
|
+
ActiveStorage.collected_assets.each_value do |asset|
|
64
|
+
build_path = File.join(Parklife.application.config.build_dir, asset.url)
|
65
|
+
FileUtils.mkdir_p(File.dirname(build_path))
|
66
|
+
FileUtils.cp(asset.blob_path, build_path)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Parklife
|
3
|
+
module Rails
|
4
|
+
module ConfigRefinements
|
5
|
+
# When setting Parklife's base also configure the Rails app's
|
6
|
+
# default_url_options and relative_url_root to match.
|
7
|
+
def base=(value)
|
8
|
+
super.tap { |uri|
|
9
|
+
app.default_url_options = {
|
10
|
+
host: Utils.host_with_port(uri),
|
11
|
+
protocol: uri.scheme,
|
12
|
+
}
|
13
|
+
|
14
|
+
base_path = !uri.path.empty? && uri.path != '/' ? uri.path : nil
|
15
|
+
ActionController::Base.relative_url_root = base_path
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/parklife/rails.rb
CHANGED
@@ -1,70 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'parklife'
|
3
|
-
require 'rails'
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
app.default_url_options = {
|
12
|
-
host: Utils.host_with_port(uri),
|
13
|
-
protocol: uri.scheme,
|
14
|
-
}
|
15
|
-
|
16
|
-
base_path = !uri.path.empty? && uri.path != '/' ? uri.path : nil
|
17
|
-
ActionController::Base.relative_url_root = base_path
|
18
|
-
}
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
module RailsRouteSetRefinements
|
23
|
-
def default_url_options
|
24
|
-
::Rails.application.default_url_options
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
class Railtie < ::Rails::Railtie
|
29
|
-
initializer 'parklife.disable_host_authorization' do |app|
|
30
|
-
# The offending middleware is included in Rails (6+) development mode and
|
31
|
-
# rejects a request with a 403 response if its host isn't present in the
|
32
|
-
# allowlist (a security feature). This prevents Parklife from working in
|
33
|
-
# a Rails app out of the box unless you manually add the expected
|
34
|
-
# Parklife base to the hosts allowlist or set it to nil to disable it -
|
35
|
-
# both of which aren't great because they disable the security feature
|
36
|
-
# whenever the development server is booted.
|
37
|
-
#
|
38
|
-
# https://guides.rubyonrails.org/configuring.html#actiondispatch-hostauthorization
|
39
|
-
#
|
40
|
-
# However it's safe to remove the middleware at this point because it
|
41
|
-
# won't be executed in the normal Rails development flow, only via a
|
42
|
-
# Parkfile when parklife/rails is required.
|
43
|
-
if defined?(ActionDispatch::HostAuthorization)
|
44
|
-
app.middleware.delete(ActionDispatch::HostAuthorization)
|
45
|
-
end
|
46
|
-
|
47
|
-
Parklife.application.config.app = app
|
48
|
-
|
49
|
-
# Allow use of the Rails application's route helpers when defining
|
50
|
-
# Parklife routes in the block form.
|
51
|
-
Parklife.application.routes.singleton_class.include(RailsRouteSetRefinements)
|
52
|
-
Parklife.application.routes.singleton_class.include(app.routes.url_helpers)
|
53
|
-
|
54
|
-
Parklife.application.config.extend(RailsConfigRefinements)
|
55
|
-
end
|
56
|
-
|
57
|
-
config.after_initialize do |app|
|
58
|
-
# Read the Rails app's URL config and apply it to Parklife's so that the
|
59
|
-
# Rails config can be used as the single source of truth.
|
60
|
-
host, port, protocol = app.default_url_options.values_at(:host, :port, :protocol)
|
61
|
-
protocol = 'https' if app.config.force_ssl
|
62
|
-
path = ActionController::Base.relative_url_root
|
63
|
-
|
64
|
-
Parklife.application.config.base.scheme = protocol if protocol
|
65
|
-
Parklife.application.config.base.host = host if host
|
66
|
-
Parklife.application.config.base.port = port if port
|
67
|
-
Parklife.application.config.base.path = path if path
|
68
|
-
end
|
69
|
-
end
|
3
|
+
# Only require the build integration when running from a Parklife CLI command.
|
4
|
+
#
|
5
|
+
# This means that the gem can safely be included in the app's Gemfile without
|
6
|
+
# applying any of its build-time tweaks.
|
7
|
+
if defined?(Parklife::CLI)
|
8
|
+
require_relative 'rails/build_integration'
|
70
9
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: parklife-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Pickles
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: parklife
|
@@ -38,7 +37,6 @@ dependencies:
|
|
38
37
|
- - ">="
|
39
38
|
- !ruby/object:Gem::Version
|
40
39
|
version: '0'
|
41
|
-
description:
|
42
40
|
email:
|
43
41
|
- spideryoung@gmail.com
|
44
42
|
executables: []
|
@@ -51,12 +49,20 @@ files:
|
|
51
49
|
- LICENSE.txt
|
52
50
|
- README.md
|
53
51
|
- Rakefile
|
52
|
+
- app/controllers/parklife/rails/active_storage/blobs_controller.rb
|
53
|
+
- config/routes.rb
|
54
54
|
- gemfiles/rails_7.0.gemfile
|
55
55
|
- gemfiles/rails_7.1.gemfile
|
56
56
|
- gemfiles/rails_7.2.gemfile
|
57
57
|
- gemfiles/rails_8.0.gemfile
|
58
|
+
- lib/active_storage/service/parklife_service.rb
|
58
59
|
- lib/parklife-rails.rb
|
60
|
+
- lib/parklife-rails/activestorage.rb
|
59
61
|
- lib/parklife/rails.rb
|
62
|
+
- lib/parklife/rails/activestorage.rb
|
63
|
+
- lib/parklife/rails/build_integration.rb
|
64
|
+
- lib/parklife/rails/config_refinements.rb
|
65
|
+
- lib/parklife/rails/route_set_refinements.rb
|
60
66
|
- lib/parklife/rails/version.rb
|
61
67
|
homepage: https://parklife.dev/rails
|
62
68
|
licenses:
|
@@ -66,7 +72,6 @@ metadata:
|
|
66
72
|
homepage_uri: https://parklife.dev/rails
|
67
73
|
rubygems_mfa_required: 'true'
|
68
74
|
source_code_uri: https://github.com/benpickles/parklife-rails
|
69
|
-
post_install_message:
|
70
75
|
rdoc_options: []
|
71
76
|
require_paths:
|
72
77
|
- lib
|
@@ -81,8 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
86
|
- !ruby/object:Gem::Version
|
82
87
|
version: '0'
|
83
88
|
requirements: []
|
84
|
-
rubygems_version: 3.1
|
85
|
-
signing_key:
|
89
|
+
rubygems_version: 3.7.1
|
86
90
|
specification_version: 4
|
87
91
|
summary: Rails integration for Parklife
|
88
92
|
test_files: []
|