studio-engine 0.5.1 → 0.5.2
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 +17 -1
- data/README.md +34 -21
- data/lib/studio/engine.rb +4 -0
- data/lib/studio/mail_transport.rb +82 -0
- data/lib/studio/version.rb +1 -1
- data/lib/studio.rb +1 -0
- data/lib/tasks/studio_ses.rake +103 -0
- data/studio-engine.gemspec +5 -4
- metadata +39 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f351e5f2cf75e05dfc8befdd34cb525310ab4c4415bb084d15c57a1e3713b782
|
|
4
|
+
data.tar.gz: f84aad6087299527b4cdb330e644ee707f695b6a70597c753039c8b7af81ee93
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fc184764831f825771d76f9cc93ae51f0103823c571ad886431beeffeb1ba55c15907004dc1796d6901fdafc560571c02cbde8fdd02141f2629c5cd60071f5a0
|
|
7
|
+
data.tar.gz: 20a76451338e9aea84191a1c5f3efb995c46514117e18e80d014bda75524ff50e6288abb88da26b84ce463fd296f601e63580de9bfb729458c31cfaf54fa96dc
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — `MAJOR.MINOR.PATCH`.
|
|
3
|
+
The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — `MAJOR.MINOR.PATCH`. Consumer Rails apps install the released RubyGems package with `gem "studio-engine", "~> 0.5"`; bumping the gem version and updating consumer lockfiles is a release.
|
|
4
|
+
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
No entries yet.
|
|
8
|
+
|
|
9
|
+
## v0.5.2 (2026-06-13)
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **`Studio::MailTransport`** — shared ActionMailer transport selection for SES SMTP primary and Resend rollback.
|
|
13
|
+
- **`ses:check` / `ses:verify_domain`** — shared Rake tasks for SES credential and domain verification checks.
|
|
14
|
+
- **`bin/release-check`** — local preflight for Ruby syntax, engine unit tests, and optional gem packaging.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Engine release docs now describe the RubyGems flow and consumer lockfile adoption instead of the legacy git tag pinning flow.
|
|
18
|
+
- Runtime dependency ranges are now bounded for cleaner RubyGems releases while preserving the current Rails 7 / Solid Queue 1.x app stack.
|
|
19
|
+
- `resend` is declared as an engine runtime dependency so consumer apps can drop direct rollback dependencies after they bundle the release that includes `Studio::MailTransport`.
|
|
4
20
|
|
|
5
21
|
## v0.5.1 (2026-06-02)
|
|
6
22
|
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Studio Engine
|
|
2
2
|
|
|
3
|
-
Shared Rails engine for McRitchie apps. Provides authentication, error handling, dynamic theming, and common concerns used by [McRitchie Studio](https://app.mcritchie.studio) and [Turf Monster](https://
|
|
3
|
+
Shared Rails engine for McRitchie apps. Provides authentication, error handling, dynamic theming, and common concerns used by [McRitchie Studio](https://app.mcritchie.studio) and [Turf Monster](https://app.turfmonster.media).
|
|
4
4
|
|
|
5
5
|
> **Part of the McRitchie ecosystem** — see [`ECOSYSTEM.md`](https://github.com/amcritchie/mcritchie-studio/blob/main/docs/ECOSYSTEM.md) for the 5-repo map; [`house-burn-down.md`](https://github.com/amcritchie/mcritchie-studio/blob/main/docs/agents/system/house-burn-down.md) for fresh-Mac recovery.
|
|
6
6
|
|
|
@@ -8,16 +8,16 @@ Shared Rails engine for McRitchie apps. Provides authentication, error handling,
|
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
10
|
# Gemfile — install from RubyGems (recommended)
|
|
11
|
-
gem "studio-engine", "~> 0.
|
|
11
|
+
gem "studio-engine", "~> 0.5"
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Then `bundle install`. The current release is **v0.
|
|
14
|
+
Then `bundle install`. The current release is **v0.5.2**; see [`CHANGELOG.md`](./CHANGELOG.md) for the history.
|
|
15
15
|
|
|
16
|
-
> Published to RubyGems as of v0.4.0 (2026-05-17).
|
|
16
|
+
> Published to RubyGems as of v0.4.0 (2026-05-17). New installs should use the RubyGems form, which the consumer Rails apps (`mcritchie-studio`, `turf-monster`) already use.
|
|
17
17
|
|
|
18
18
|
## What It Provides
|
|
19
19
|
|
|
20
|
-
- **Authentication**:
|
|
20
|
+
- **Authentication**: Passwordless magic-link auth, optional password auth, Google OAuth via OmniAuth, Solana wallet sign-in, and optional one-way SSO patterns
|
|
21
21
|
- **Error handling**: `Studio::ErrorHandling` concern with `rescue_and_log`, `ErrorLog` model with `capture!`, error log viewer at `/error_logs`
|
|
22
22
|
- **Theme system**: Dynamic CSS custom properties generated from 7 role colors (primary, dark, light, success, accent, warning, danger). Dark/light mode toggle. Admin theme editor at `/admin/theme`.
|
|
23
23
|
- **Sluggable concern**: `before_save :set_slug` with `to_param` for human-readable URLs
|
|
@@ -31,14 +31,26 @@ Each consuming app configures the engine in `config/initializers/studio.rb`:
|
|
|
31
31
|
Studio.configure do |config|
|
|
32
32
|
config.app_name = "My App"
|
|
33
33
|
config.session_key = :my_app_user_id
|
|
34
|
-
config.sso_logo = "/logo.svg"
|
|
35
34
|
config.welcome_message = ->(user) { "Welcome, #{user.display_name}!" }
|
|
36
|
-
config.
|
|
35
|
+
config.auth_methods = %i[magic_link google]
|
|
36
|
+
config.registration_params = [:name, :email]
|
|
37
|
+
config.magic_link_token_name = "magic_link_my_app_v1"
|
|
38
|
+
config.mailer_from = ENV.fetch("MAILER_FROM", "noreply@example.com")
|
|
37
39
|
config.theme_primary = "#4BAF50" # Override default violet
|
|
38
40
|
config.theme_logos = ["logo.svg"]
|
|
39
41
|
end
|
|
40
42
|
```
|
|
41
43
|
|
|
44
|
+
Transactional mail transport is shared through `Studio::MailTransport`:
|
|
45
|
+
|
|
46
|
+
```ruby
|
|
47
|
+
# config/initializers/studio_mail_transport.rb
|
|
48
|
+
Studio::MailTransport.configure!
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
It selects SES SMTP when `MAIL_TRANSPORT=ses` and SES SMTP credentials are
|
|
52
|
+
present, otherwise falls back to Resend when `RESEND_API_KEY` is present.
|
|
53
|
+
|
|
42
54
|
## Routes
|
|
43
55
|
|
|
44
56
|
In the consuming app's `config/routes.rb`:
|
|
@@ -50,7 +62,7 @@ Rails.application.routes.draw do
|
|
|
50
62
|
end
|
|
51
63
|
```
|
|
52
64
|
|
|
53
|
-
This draws
|
|
65
|
+
This draws the enabled auth routes (`/login`, `/signup`, `/logout`, magic-link routes, Solana routes), OAuth callbacks, optional SSO routes, `/error_logs`, and `/admin/theme`.
|
|
54
66
|
|
|
55
67
|
## Overriding Views
|
|
56
68
|
|
|
@@ -58,19 +70,20 @@ This is a non-isolated engine -- app views at the same path automatically overri
|
|
|
58
70
|
|
|
59
71
|
## Releasing
|
|
60
72
|
|
|
61
|
-
Engine releases
|
|
73
|
+
Engine releases use semantic versions and are published to RubyGems. The full
|
|
74
|
+
operator checklist lives in [`docs/RELEASE.md`](./docs/RELEASE.md).
|
|
75
|
+
|
|
76
|
+
Short form:
|
|
62
77
|
|
|
63
|
-
1.
|
|
64
|
-
2.
|
|
65
|
-
3.
|
|
66
|
-
4.
|
|
67
|
-
5.
|
|
68
|
-
|
|
69
|
-
7. In each consumer app's Gemfile, update the `tag:` field. Commit + push.
|
|
70
|
-
8. On consumer prod: `bundle install` runs as part of the deploy buildpack.
|
|
78
|
+
1. Update [`CHANGELOG.md`](./CHANGELOG.md) and `lib/studio/version.rb`.
|
|
79
|
+
2. Run `bin/release-check --build`.
|
|
80
|
+
3. Publish the gem only after explicit approval.
|
|
81
|
+
4. Tag the release after RubyGems accepts the gem.
|
|
82
|
+
5. In each consumer app, run `bundle update studio-engine`, verify the lockfile,
|
|
83
|
+
and run app smoke checks.
|
|
71
84
|
|
|
72
85
|
**Semver guide**
|
|
73
|
-
- **PATCH**: bug fix; no API change. Consumers can
|
|
86
|
+
- **PATCH**: bug fix; no API change. Consumers can update the gem with zero diff elsewhere.
|
|
74
87
|
- **MINOR**: backward-compatible feature add. Consumers may opt in to new APIs.
|
|
75
88
|
- **MAJOR**: breaking change. Consumers will need code changes alongside the tag bump.
|
|
76
89
|
|
|
@@ -83,11 +96,11 @@ When iterating on engine code from a consumer app, point bundler at the local pa
|
|
|
83
96
|
bundle config set --local local.studio /Users/alex/projects/studio-engine
|
|
84
97
|
bundle install
|
|
85
98
|
# ... iterate in both repos ...
|
|
86
|
-
bundle config unset --local local.studio # restore
|
|
99
|
+
bundle config unset --local local.studio # restore RubyGems resolution
|
|
87
100
|
```
|
|
88
101
|
|
|
89
|
-
|
|
102
|
+
For short local experiments, temporarily point a consumer Gemfile at `path: "../studio-engine"` and restore the RubyGems dependency before merging.
|
|
90
103
|
|
|
91
104
|
## Development Notes
|
|
92
105
|
|
|
93
|
-
See [CLAUDE.md](./CLAUDE.md)
|
|
106
|
+
See [CLAUDE.md](./CLAUDE.md) only as legacy migration context while neutral agent docs are being extracted. Current cross-repo setup, ports, credentials, and workflow guidance live in McRitchie Studio's [`docs/agents/`](https://github.com/amcritchie/mcritchie-studio/tree/main/docs/agents).
|
data/lib/studio/engine.rb
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
module Studio
|
|
2
2
|
class Engine < ::Rails::Engine
|
|
3
|
+
rake_tasks do
|
|
4
|
+
load File.expand_path("../tasks/studio_ses.rake", __dir__)
|
|
5
|
+
end
|
|
6
|
+
|
|
3
7
|
config.after_initialize do
|
|
4
8
|
# Validate the host app's User model satisfies the engine's contract.
|
|
5
9
|
# See docs/USER_CONTRACT.md. Opt out with Studio.validate_user_contract = false.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Studio
|
|
4
|
+
class MailTransport
|
|
5
|
+
Result = Struct.new(:transport, :delivery_method, :message, keyword_init: true)
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def configure!(env: ENV,
|
|
9
|
+
rails_env: defined?(Rails) ? Rails.env : "development",
|
|
10
|
+
action_mailer: defined?(ActionMailer) ? ActionMailer::Base : nil,
|
|
11
|
+
logger: defined?(Rails) ? Rails.logger : nil,
|
|
12
|
+
mailer_from: defined?(Studio) && Studio.respond_to?(:mailer_from) ? Studio.mailer_from : nil,
|
|
13
|
+
resend_loader: method(:load_resend!),
|
|
14
|
+
resend_configurer: method(:configure_resend!))
|
|
15
|
+
raise ArgumentError, "action_mailer is required" unless action_mailer
|
|
16
|
+
|
|
17
|
+
if rails_env.to_s == "test"
|
|
18
|
+
return Result.new(transport: :test, delivery_method: action_mailer.delivery_method, message: "test environment skipped")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
selected = env["MAIL_TRANSPORT"].to_s.downcase
|
|
22
|
+
ses_ready = selected == "ses" && present?(env["SES_SMTP_USERNAME"]) && present?(env["SES_SMTP_PASSWORD"])
|
|
23
|
+
|
|
24
|
+
if selected == "ses" && !ses_ready
|
|
25
|
+
log(logger, :warn, "[mail] MAIL_TRANSPORT=ses but SES_SMTP_USERNAME/PASSWORD missing - keeping fallback transport")
|
|
26
|
+
elsif present?(selected) && !%w[ses resend].include?(selected)
|
|
27
|
+
log(logger, :warn, "[mail] unknown MAIL_TRANSPORT=#{selected.inspect} - keeping fallback transport")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if ses_ready
|
|
31
|
+
configure_ses!(env: env, action_mailer: action_mailer)
|
|
32
|
+
region = env.fetch("SES_REGION", "us-east-2")
|
|
33
|
+
log(logger, :info, "[mail] transport=SES region=#{region} from=#{mailer_from}")
|
|
34
|
+
return Result.new(transport: :ses, delivery_method: action_mailer.delivery_method, message: "SES SMTP active")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
if present?(env["RESEND_API_KEY"])
|
|
38
|
+
resend_loader.call
|
|
39
|
+
resend_configurer.call(env["RESEND_API_KEY"])
|
|
40
|
+
action_mailer.delivery_method = :resend
|
|
41
|
+
log(logger, :info, "[mail] transport=Resend from=#{mailer_from}")
|
|
42
|
+
return Result.new(transport: :resend, delivery_method: action_mailer.delivery_method, message: "Resend active")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Result.new(transport: :default, delivery_method: action_mailer.delivery_method, message: "no transactional transport configured")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def configure_ses!(env:, action_mailer:)
|
|
49
|
+
region = env.fetch("SES_REGION", "us-east-2")
|
|
50
|
+
action_mailer.delivery_method = :smtp
|
|
51
|
+
action_mailer.smtp_settings = {
|
|
52
|
+
address: env.fetch("SES_SMTP_HOST", "email-smtp.#{region}.amazonaws.com"),
|
|
53
|
+
port: env.fetch("SES_SMTP_PORT", 587).to_i,
|
|
54
|
+
user_name: env["SES_SMTP_USERNAME"],
|
|
55
|
+
password: env["SES_SMTP_PASSWORD"],
|
|
56
|
+
authentication: :login,
|
|
57
|
+
enable_starttls_auto: true
|
|
58
|
+
}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def load_resend!
|
|
62
|
+
require "resend"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def configure_resend!(api_key)
|
|
66
|
+
Resend.api_key = api_key
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def present?(value)
|
|
72
|
+
!value.to_s.strip.empty?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def log(logger, level, message)
|
|
76
|
+
return unless logger
|
|
77
|
+
|
|
78
|
+
logger.public_send(level, message)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
data/lib/studio/version.rb
CHANGED
data/lib/studio.rb
CHANGED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
unless Rake::Task.task_defined?("ses:check")
|
|
4
|
+
namespace :ses do
|
|
5
|
+
def studio_ses_signer(region)
|
|
6
|
+
require "aws-sigv4"
|
|
7
|
+
|
|
8
|
+
Aws::Sigv4::Signer.new(
|
|
9
|
+
service: "ses",
|
|
10
|
+
region: region,
|
|
11
|
+
access_key_id: ENV.fetch("AWS_ACCESS_KEY_ID"),
|
|
12
|
+
secret_access_key: ENV.fetch("AWS_SECRET_ACCESS_KEY")
|
|
13
|
+
)
|
|
14
|
+
rescue LoadError
|
|
15
|
+
abort "ses:* needs aws-sigv4; it is usually available through aws-sdk-s3."
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def studio_ses_request(signer, region, method, path, body = nil)
|
|
19
|
+
require "net/http"
|
|
20
|
+
require "json"
|
|
21
|
+
|
|
22
|
+
url = "https://email.#{region}.amazonaws.com#{path}"
|
|
23
|
+
headers = body ? { "content-type" => "application/json" } : {}
|
|
24
|
+
sig = signer.sign_request(http_method: method, url: url, body: body, headers: headers)
|
|
25
|
+
uri = URI(url)
|
|
26
|
+
http = Net::HTTP.new(uri.host, 443)
|
|
27
|
+
http.use_ssl = true
|
|
28
|
+
req = Net::HTTP.const_get(method.capitalize).new(uri)
|
|
29
|
+
headers.each { |key, value| req[key] = value }
|
|
30
|
+
sig.headers.each { |key, value| req[key] = value }
|
|
31
|
+
req.body = body if body
|
|
32
|
+
res = http.request(req)
|
|
33
|
+
[res.code.to_i, (JSON.parse(res.body) rescue { "raw" => res.body.to_s[0, 300] })]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def studio_ses_error(body)
|
|
37
|
+
body["message"] || body["Message"] || body["raw"]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
desc "Check SES account status and verified identities"
|
|
41
|
+
task check: :environment do
|
|
42
|
+
region = ENV.fetch("SES_REGION", "us-east-2")
|
|
43
|
+
signer = studio_ses_signer(region)
|
|
44
|
+
get = ->(path) { studio_ses_request(signer, region, "GET", path) }
|
|
45
|
+
|
|
46
|
+
code, account = get.call("/v2/email/account")
|
|
47
|
+
puts "GetAccount (region #{region}) -> HTTP #{code}"
|
|
48
|
+
if code == 200
|
|
49
|
+
puts " SendingEnabled=#{account['SendingEnabled']} ProductionAccessEnabled=#{account['ProductionAccessEnabled']} Enforcement=#{account['EnforcementStatus']}"
|
|
50
|
+
else
|
|
51
|
+
puts " ERROR: #{studio_ses_error(account)}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
code, identities = get.call("/v2/email/identities")
|
|
55
|
+
puts "ListEmailIdentities -> HTTP #{code}"
|
|
56
|
+
if code == 200
|
|
57
|
+
list = identities["EmailIdentities"] || []
|
|
58
|
+
names = list.map { |identity| "#{identity['IdentityName']}(#{identity['VerifiedForSendingStatus'] ? 'verified' : 'pending'})" }
|
|
59
|
+
puts " identities: #{names.empty? ? '(none yet)' : names.join(', ')}"
|
|
60
|
+
else
|
|
61
|
+
puts " ERROR: #{studio_ses_error(identities)}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
transport = ENV.fetch("MAIL_TRANSPORT", "(unset -> resend)")
|
|
65
|
+
puts "Live transport: MAIL_TRANSPORT=#{transport} delivery_method=#{ActionMailer::Base.delivery_method}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
desc "Create a SES domain identity and print DKIM CNAME records"
|
|
69
|
+
task :verify_domain, [:domain] => :environment do |_task, args|
|
|
70
|
+
abort "Usage: ses:verify_domain[domain]" if args[:domain].blank?
|
|
71
|
+
|
|
72
|
+
domain = args[:domain]
|
|
73
|
+
region = ENV.fetch("SES_REGION", "us-east-2")
|
|
74
|
+
signer = studio_ses_signer(region)
|
|
75
|
+
body = { EmailIdentity: domain }.to_json
|
|
76
|
+
code, response = studio_ses_request(signer, region, "POST", "/v2/email/identities", body)
|
|
77
|
+
|
|
78
|
+
if code == 409 || "#{response['message']}#{response['Message']}".match?(/already exists/i)
|
|
79
|
+
code, response = studio_ses_request(signer, region, "GET", "/v2/email/identities/#{domain}")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
if code != 200
|
|
83
|
+
puts "#{domain}: ERROR (HTTP #{code}) #{studio_ses_error(response)}"
|
|
84
|
+
next
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
tokens = response.dig("DkimAttributes", "Tokens") || []
|
|
88
|
+
status = response.dig("DkimAttributes", "Status") || response["VerifiedForSendingStatus"]
|
|
89
|
+
puts "== #{domain} (region #{region}, DKIM status: #{status}) =="
|
|
90
|
+
if tokens.empty?
|
|
91
|
+
puts " (no DKIM tokens returned)"
|
|
92
|
+
else
|
|
93
|
+
puts " Add these 3 CNAME records to #{domain}'s DNS:"
|
|
94
|
+
tokens.each do |token|
|
|
95
|
+
puts " NAME: #{token}._domainkey.#{domain}"
|
|
96
|
+
puts " TYPE: CNAME"
|
|
97
|
+
puts " VALUE: #{token}.dkim.amazonses.com"
|
|
98
|
+
puts ""
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/studio-engine.gemspec
CHANGED
|
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
|
|
|
13
13
|
|
|
14
14
|
spec.metadata = {
|
|
15
15
|
"homepage_uri" => "https://github.com/amcritchie/studio-engine",
|
|
16
|
-
"source_code_uri" => "https://github.com/amcritchie/studio-engine",
|
|
16
|
+
"source_code_uri" => "https://github.com/amcritchie/studio-engine/tree/main",
|
|
17
17
|
"bug_tracker_uri" => "https://github.com/amcritchie/studio-engine/issues",
|
|
18
18
|
"changelog_uri" => "https://github.com/amcritchie/studio-engine/blob/main/CHANGELOG.md"
|
|
19
19
|
}
|
|
@@ -21,10 +21,11 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
spec.files = Dir["lib/**/*", "app/**/*", "config/**/*", "tailwind/**/*", "Gemfile", "studio-engine.gemspec", "README.md", "CHANGELOG.md", "LICENSE"]
|
|
22
22
|
spec.require_paths = ["lib"]
|
|
23
23
|
|
|
24
|
-
spec.add_dependency "rails", ">= 7.0"
|
|
24
|
+
spec.add_dependency "rails", ">= 7.0", "< 8.0"
|
|
25
25
|
spec.add_dependency "tailwindcss-rails", "~> 2.7"
|
|
26
|
-
spec.add_dependency "faker", ">= 2.0"
|
|
27
|
-
spec.add_dependency "solid_queue"
|
|
26
|
+
spec.add_dependency "faker", ">= 2.0", "< 4.0"
|
|
27
|
+
spec.add_dependency "solid_queue", ">= 1.0", "< 2.0"
|
|
28
28
|
spec.add_dependency "aws-sdk-s3", "~> 1.218"
|
|
29
29
|
spec.add_dependency "mini_magick", "~> 5.0"
|
|
30
|
+
spec.add_dependency "resend", "~> 1.1"
|
|
30
31
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: studio-engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -17,6 +17,9 @@ dependencies:
|
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '7.0'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '8.0'
|
|
20
23
|
type: :runtime
|
|
21
24
|
prerelease: false
|
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -24,6 +27,9 @@ dependencies:
|
|
|
24
27
|
- - ">="
|
|
25
28
|
- !ruby/object:Gem::Version
|
|
26
29
|
version: '7.0'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '8.0'
|
|
27
33
|
- !ruby/object:Gem::Dependency
|
|
28
34
|
name: tailwindcss-rails
|
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -45,6 +51,9 @@ dependencies:
|
|
|
45
51
|
- - ">="
|
|
46
52
|
- !ruby/object:Gem::Version
|
|
47
53
|
version: '2.0'
|
|
54
|
+
- - "<"
|
|
55
|
+
- !ruby/object:Gem::Version
|
|
56
|
+
version: '4.0'
|
|
48
57
|
type: :runtime
|
|
49
58
|
prerelease: false
|
|
50
59
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -52,20 +61,29 @@ dependencies:
|
|
|
52
61
|
- - ">="
|
|
53
62
|
- !ruby/object:Gem::Version
|
|
54
63
|
version: '2.0'
|
|
64
|
+
- - "<"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '4.0'
|
|
55
67
|
- !ruby/object:Gem::Dependency
|
|
56
68
|
name: solid_queue
|
|
57
69
|
requirement: !ruby/object:Gem::Requirement
|
|
58
70
|
requirements:
|
|
59
71
|
- - ">="
|
|
60
72
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
73
|
+
version: '1.0'
|
|
74
|
+
- - "<"
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '2.0'
|
|
62
77
|
type: :runtime
|
|
63
78
|
prerelease: false
|
|
64
79
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
80
|
requirements:
|
|
66
81
|
- - ">="
|
|
67
82
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
83
|
+
version: '1.0'
|
|
84
|
+
- - "<"
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '2.0'
|
|
69
87
|
- !ruby/object:Gem::Dependency
|
|
70
88
|
name: aws-sdk-s3
|
|
71
89
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -94,6 +112,20 @@ dependencies:
|
|
|
94
112
|
- - "~>"
|
|
95
113
|
- !ruby/object:Gem::Version
|
|
96
114
|
version: '5.0'
|
|
115
|
+
- !ruby/object:Gem::Dependency
|
|
116
|
+
name: resend
|
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
|
118
|
+
requirements:
|
|
119
|
+
- - "~>"
|
|
120
|
+
- !ruby/object:Gem::Version
|
|
121
|
+
version: '1.1'
|
|
122
|
+
type: :runtime
|
|
123
|
+
prerelease: false
|
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
125
|
+
requirements:
|
|
126
|
+
- - "~>"
|
|
127
|
+
- !ruby/object:Gem::Version
|
|
128
|
+
version: '1.1'
|
|
97
129
|
description: Studio Engine is a non-isolated Rails engine that ships an opinionated
|
|
98
130
|
authentication + SSO contract, a polymorphic ErrorLog model, a Sluggable concern,
|
|
99
131
|
a 7-role dynamic theme system with CSS-custom-property generation, and an S3-backed
|
|
@@ -172,10 +204,12 @@ files:
|
|
|
172
204
|
- lib/studio/color_scale.rb
|
|
173
205
|
- lib/studio/engine.rb
|
|
174
206
|
- lib/studio/image_cache.rb
|
|
207
|
+
- lib/studio/mail_transport.rb
|
|
175
208
|
- lib/studio/s3.rb
|
|
176
209
|
- lib/studio/theme_resolver.rb
|
|
177
210
|
- lib/studio/username_generator.rb
|
|
178
211
|
- lib/studio/version.rb
|
|
212
|
+
- lib/tasks/studio_ses.rake
|
|
179
213
|
- studio-engine.gemspec
|
|
180
214
|
- tailwind/studio.tailwind.config.js
|
|
181
215
|
homepage: https://github.com/amcritchie/studio-engine
|
|
@@ -183,7 +217,7 @@ licenses:
|
|
|
183
217
|
- MIT
|
|
184
218
|
metadata:
|
|
185
219
|
homepage_uri: https://github.com/amcritchie/studio-engine
|
|
186
|
-
source_code_uri: https://github.com/amcritchie/studio-engine
|
|
220
|
+
source_code_uri: https://github.com/amcritchie/studio-engine/tree/main
|
|
187
221
|
bug_tracker_uri: https://github.com/amcritchie/studio-engine/issues
|
|
188
222
|
changelog_uri: https://github.com/amcritchie/studio-engine/blob/main/CHANGELOG.md
|
|
189
223
|
post_install_message:
|