better_auth-hanami 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/CHANGELOG.md +12 -0
- data/LICENSE.md +22 -0
- data/README.md +153 -0
- data/lib/better_auth/hanami/action_helpers.rb +74 -0
- data/lib/better_auth/hanami/configuration.rb +53 -0
- data/lib/better_auth/hanami/generators/install_generator.rb +130 -0
- data/lib/better_auth/hanami/generators/migration_generator.rb +50 -0
- data/lib/better_auth/hanami/generators/relation_generator.rb +130 -0
- data/lib/better_auth/hanami/migration.rb +99 -0
- data/lib/better_auth/hanami/mounted_app.rb +36 -0
- data/lib/better_auth/hanami/routing.rb +34 -0
- data/lib/better_auth/hanami/sequel_adapter.rb +288 -0
- data/lib/better_auth/hanami/version.rb +7 -0
- data/lib/better_auth/hanami.rb +35 -0
- data/lib/better_auth_hanami.rb +3 -0
- data/lib/tasks/better_auth.rake +22 -0
- metadata +233 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f1d70c9e2bfc595306586a4978e2de0b8cd160b1ce1a96c60f2aa49afbe35343
|
|
4
|
+
data.tar.gz: d167171822dbb43b4369b6b8c8b9dfbacd45dce56275fb6a8232b5b198c426d7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 01033aa8d2c01ac516572fa5fdc6b455869fcaa6484569fdfe7788127a98f0612cb6aa1a03c65bb10b906066deaa48dfa00b5beea1f3e4c44988d6b08958e834
|
|
7
|
+
data.tar.gz: b64774ddd9392d6911fd6647aaf34506ee1b10035bb9067a0c1cc3b0d0c7a949215b37c349e7177a6d22ae5a54eeaef3ff2ff0b029e8a298e97180235e4b8f68
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Initial Hanami 2.3+ adapter with Rack route mounting, Sequel persistence, ROM::SQL migration rendering, action helpers, and Rake/generator commands.
|
data/LICENSE.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
The MIT License (MIT)
|
|
4
|
+
Copyright (c) 2024 - present
|
|
5
|
+
|
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
7
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
8
|
+
the Software without restriction, including without limitation the rights to
|
|
9
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
10
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
11
|
+
subject to the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
20
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
21
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
|
22
|
+
OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Better Auth Hanami
|
|
2
|
+
|
|
3
|
+
Hanami adapter for Better Auth Ruby. It mounts the core Rack auth object inside
|
|
4
|
+
Hanami, uses Hanami's ROM/Sequel database gateway for persistence, renders
|
|
5
|
+
ROM::SQL migrations, generates Hanami relations/repos for app queries, and
|
|
6
|
+
provides action helpers plus generator tasks.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
gem "better_auth-hanami"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
bundle install
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Setup
|
|
19
|
+
|
|
20
|
+
Load the task file from your app Rakefile if your app does not already load
|
|
21
|
+
`lib/tasks`:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
# Rakefile
|
|
25
|
+
require "better_auth/hanami"
|
|
26
|
+
load Gem.loaded_specs.fetch("better_auth-hanami").full_gem_path + "/lib/tasks/better_auth.rake"
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Generate the provider, route wiring, task wrapper, settings, relations/repos,
|
|
30
|
+
and base migration:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bundle exec rake better_auth:init
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Run Hanami migrations:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bin/hanami db migrate
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
When you add plugins that introduce schema tables or fields, regenerate both
|
|
43
|
+
the migration and the app query objects before migrating a new app:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
bundle exec rake better_auth:generate:migration
|
|
47
|
+
bundle exec rake better_auth:generate:relations
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
The install generator creates `config/providers/better_auth.rb`:
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
Hanami.app.register_provider(:better_auth) do
|
|
56
|
+
prepare do
|
|
57
|
+
require "better_auth/hanami"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
start do
|
|
61
|
+
BetterAuth::Hanami.configure do |config|
|
|
62
|
+
config.secret = target["settings"].better_auth_secret
|
|
63
|
+
config.base_url = target["settings"].better_auth_url
|
|
64
|
+
config.base_path = "/api/auth"
|
|
65
|
+
config.database = ->(options) {
|
|
66
|
+
BetterAuth::Hanami::SequelAdapter.from_container(target, options)
|
|
67
|
+
}
|
|
68
|
+
config.email_and_password = {enabled: true}
|
|
69
|
+
config.plugins = []
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
auth = BetterAuth::Hanami.auth
|
|
73
|
+
register "better_auth.auth", auth
|
|
74
|
+
register "better_auth.rack_app", BetterAuth::Hanami::MountedApp.new(auth, mount_path: BetterAuth::Hanami.configuration.base_path)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Routes
|
|
80
|
+
|
|
81
|
+
The generated `config/routes.rb` includes:
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
require "better_auth/hanami/routing"
|
|
85
|
+
|
|
86
|
+
module Bookshelf
|
|
87
|
+
class Routes < Hanami::Routes
|
|
88
|
+
include BetterAuth::Hanami::Routing
|
|
89
|
+
|
|
90
|
+
better_auth
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
By default this mounts Better Auth at `/api/auth`. Customize the path:
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
better_auth at: "/auth"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Action Helpers
|
|
102
|
+
|
|
103
|
+
Include helpers in your base action:
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
class Action < Hanami::Action
|
|
107
|
+
include BetterAuth::Hanami::ActionHelpers
|
|
108
|
+
end
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Use them from an action:
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
def handle(request, response)
|
|
115
|
+
return unless require_authentication(request, response)
|
|
116
|
+
|
|
117
|
+
response.body = current_user(request).fetch("email")
|
|
118
|
+
end
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Relations And Repos
|
|
122
|
+
|
|
123
|
+
Better Auth uses `BetterAuth::Hanami::SequelAdapter` for its own reads and
|
|
124
|
+
writes. The generated Hanami relations/repos are for your application code when
|
|
125
|
+
you want to inspect or query Better Auth tables directly:
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
users = Hanami.app["relations.users"].to_a
|
|
129
|
+
user = Hanami.app["repos.user_repo"].users.by_pk(user_id).one
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
If you prefer a custom persistence implementation, configure it directly:
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
BetterAuth::Hanami.configure do |config|
|
|
136
|
+
config.database = ->(options) { MyBetterAuthAdapter.new(options) }
|
|
137
|
+
end
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Limitations
|
|
141
|
+
|
|
142
|
+
- Supports Hanami 2.3+ only. Better Auth core depends on Rack 3, and Hanami 2.3 is the first Hanami line that allows Rack 3.
|
|
143
|
+
- Hanami 1.x and Hanami 2.2/Rack 2 apps are out of scope for this adapter.
|
|
144
|
+
- The stable command surface is Rake/generator based. A `hanami better_auth ...` command is not exposed because the current public guides do not document a stable third-party Hanami CLI extension API.
|
|
145
|
+
- Apps created with `--skip-db` can use memory storage for development or tests, but production apps should configure Hanami DB or pass an explicit Better Auth adapter.
|
|
146
|
+
|
|
147
|
+
## Development
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
cd packages/better_auth-hanami
|
|
151
|
+
rbenv exec bundle exec rspec
|
|
152
|
+
rbenv exec bundle exec standardrb
|
|
153
|
+
```
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module Hanami
|
|
5
|
+
module ActionHelpers
|
|
6
|
+
def current_session(request)
|
|
7
|
+
data = better_auth_session_data(request)
|
|
8
|
+
data&.fetch(:session, nil) || data&.fetch("session", nil)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def current_user(request)
|
|
12
|
+
data = better_auth_session_data(request)
|
|
13
|
+
data&.fetch(:user, nil) || data&.fetch("user", nil)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def authenticated?(request)
|
|
17
|
+
!current_user(request).nil?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def require_authentication(request, response)
|
|
21
|
+
return true if authenticated?(request)
|
|
22
|
+
|
|
23
|
+
response.status = 401 if response.respond_to?(:status=)
|
|
24
|
+
false
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def better_auth_session_data(request)
|
|
30
|
+
env = request_env(request)
|
|
31
|
+
return env["better_auth.session"] if env.key?("better_auth.session")
|
|
32
|
+
|
|
33
|
+
env["better_auth.session"] = resolve_better_auth_session(request)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def resolve_better_auth_session(request)
|
|
37
|
+
context = BetterAuth::Endpoint::Context.new(
|
|
38
|
+
path: request_path(request),
|
|
39
|
+
method: request_method(request),
|
|
40
|
+
query: request_params(request),
|
|
41
|
+
body: {},
|
|
42
|
+
params: {},
|
|
43
|
+
headers: {"cookie" => request_cookie(request)},
|
|
44
|
+
context: BetterAuth::Hanami.auth.context,
|
|
45
|
+
request: request
|
|
46
|
+
)
|
|
47
|
+
BetterAuth::Session.find_current(context, disable_refresh: true)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def request_env(request)
|
|
51
|
+
request.respond_to?(:env) ? request.env : {}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def request_path(request)
|
|
55
|
+
request.respond_to?(:path) ? request.path : "/"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def request_method(request)
|
|
59
|
+
request.respond_to?(:request_method) ? request.request_method : "GET"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def request_params(request)
|
|
63
|
+
request.respond_to?(:params) ? request.params : {}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def request_cookie(request)
|
|
67
|
+
return request.get_header("HTTP_COOKIE") if request.respond_to?(:get_header)
|
|
68
|
+
|
|
69
|
+
headers = request.respond_to?(:headers) ? request.headers : {}
|
|
70
|
+
headers["cookie"] || headers["Cookie"]
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BetterAuth
|
|
4
|
+
module Hanami
|
|
5
|
+
class Configuration
|
|
6
|
+
AUTH_OPTION_NAMES = %i[
|
|
7
|
+
app_name
|
|
8
|
+
base_url
|
|
9
|
+
base_path
|
|
10
|
+
secret
|
|
11
|
+
database
|
|
12
|
+
plugins
|
|
13
|
+
trusted_origins
|
|
14
|
+
rate_limit
|
|
15
|
+
session
|
|
16
|
+
account
|
|
17
|
+
user
|
|
18
|
+
verification
|
|
19
|
+
advanced
|
|
20
|
+
email_and_password
|
|
21
|
+
password_hasher
|
|
22
|
+
email_verification
|
|
23
|
+
social_providers
|
|
24
|
+
experimental
|
|
25
|
+
secondary_storage
|
|
26
|
+
database_hooks
|
|
27
|
+
hooks
|
|
28
|
+
on_api_error
|
|
29
|
+
disabled_paths
|
|
30
|
+
logger
|
|
31
|
+
].freeze
|
|
32
|
+
|
|
33
|
+
attr_accessor(*AUTH_OPTION_NAMES)
|
|
34
|
+
|
|
35
|
+
def initialize
|
|
36
|
+
@base_path = BetterAuth::Configuration::DEFAULT_BASE_PATH
|
|
37
|
+
@plugins = []
|
|
38
|
+
@trusted_origins = []
|
|
39
|
+
@database = ->(options) { SequelAdapter.from_hanami(options) }
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def to_auth_options
|
|
43
|
+
AUTH_OPTION_NAMES.each_with_object({}) do |name, options|
|
|
44
|
+
value = public_send(name)
|
|
45
|
+
next if value.nil?
|
|
46
|
+
next if value.respond_to?(:empty?) && value.empty?
|
|
47
|
+
|
|
48
|
+
options[name] = value
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require_relative "migration_generator"
|
|
5
|
+
require_relative "relation_generator"
|
|
6
|
+
|
|
7
|
+
module BetterAuth
|
|
8
|
+
module Hanami
|
|
9
|
+
module Generators
|
|
10
|
+
class InstallGenerator
|
|
11
|
+
def initialize(destination_root: Dir.pwd)
|
|
12
|
+
@destination_root = destination_root
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
create_provider
|
|
17
|
+
create_task
|
|
18
|
+
update_routes
|
|
19
|
+
update_settings
|
|
20
|
+
RelationGenerator.new(destination_root: destination_root).run
|
|
21
|
+
MigrationGenerator.new(destination_root: destination_root).run
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
attr_reader :destination_root
|
|
27
|
+
|
|
28
|
+
def create_provider
|
|
29
|
+
path = File.join(destination_root, "config/providers/better_auth.rb")
|
|
30
|
+
return if File.exist?(path)
|
|
31
|
+
|
|
32
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
33
|
+
File.write(path, provider_template)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def create_task
|
|
37
|
+
path = File.join(destination_root, "lib/tasks/better_auth.rake")
|
|
38
|
+
return if File.exist?(path)
|
|
39
|
+
|
|
40
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
41
|
+
File.write(path, task_template)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def update_routes
|
|
45
|
+
path = File.join(destination_root, "config/routes.rb")
|
|
46
|
+
return unless File.exist?(path)
|
|
47
|
+
|
|
48
|
+
content = File.read(path)
|
|
49
|
+
content = %(require "better_auth/hanami/routing"\n) + content unless content.include?(%("better_auth/hanami/routing"))
|
|
50
|
+
unless content.include?("include BetterAuth::Hanami::Routing")
|
|
51
|
+
content = content.sub("class Routes < Hanami::Routes\n", "class Routes < Hanami::Routes\n include BetterAuth::Hanami::Routing\n better_auth\n")
|
|
52
|
+
end
|
|
53
|
+
File.write(path, content)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def update_settings
|
|
57
|
+
path = File.join(destination_root, "config/settings.rb")
|
|
58
|
+
return unless File.exist?(path)
|
|
59
|
+
|
|
60
|
+
content = File.read(path)
|
|
61
|
+
return if content.include?("setting :better_auth_secret")
|
|
62
|
+
|
|
63
|
+
insertion = [
|
|
64
|
+
" setting :better_auth_secret, constructor: Types::String.constrained(min_size: 32)",
|
|
65
|
+
" setting :better_auth_url, constructor: Types::String.optional"
|
|
66
|
+
].join("\n")
|
|
67
|
+
content = content.sub("class Settings < Hanami::Settings\n", "class Settings < Hanami::Settings\n#{insertion}\n")
|
|
68
|
+
File.write(path, content)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def provider_template
|
|
72
|
+
<<~RUBY
|
|
73
|
+
# frozen_string_literal: true
|
|
74
|
+
|
|
75
|
+
Hanami.app.register_provider(:better_auth) do
|
|
76
|
+
prepare do
|
|
77
|
+
require "better_auth/hanami"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
start do
|
|
81
|
+
BetterAuth::Hanami.configure do |config|
|
|
82
|
+
config.secret = target["settings"].better_auth_secret
|
|
83
|
+
config.base_url = target["settings"].better_auth_url
|
|
84
|
+
config.base_path = "/api/auth"
|
|
85
|
+
config.database = ->(options) {
|
|
86
|
+
BetterAuth::Hanami::SequelAdapter.from_container(target, options)
|
|
87
|
+
}
|
|
88
|
+
config.trusted_origins = [target["settings"].better_auth_url].compact
|
|
89
|
+
config.email_and_password = {enabled: true}
|
|
90
|
+
config.plugins = []
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
auth = BetterAuth::Hanami.auth
|
|
94
|
+
register "better_auth.auth", auth
|
|
95
|
+
register "better_auth.rack_app", BetterAuth::Hanami::MountedApp.new(auth, mount_path: BetterAuth::Hanami.configuration.base_path)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
RUBY
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def task_template
|
|
102
|
+
<<~RUBY
|
|
103
|
+
# frozen_string_literal: true
|
|
104
|
+
|
|
105
|
+
require "better_auth/hanami"
|
|
106
|
+
|
|
107
|
+
namespace :better_auth do
|
|
108
|
+
desc "Create Better Auth Hanami provider, routes, settings, tasks, and base migration"
|
|
109
|
+
task :init do
|
|
110
|
+
BetterAuth::Hanami::Generators::InstallGenerator.new.run
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
namespace :generate do
|
|
114
|
+
desc "Create the Better Auth Hanami base migration"
|
|
115
|
+
task :migration do
|
|
116
|
+
BetterAuth::Hanami::Generators::MigrationGenerator.new.run
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
desc "Create Hanami relations and repos for Better Auth tables"
|
|
120
|
+
task :relations do
|
|
121
|
+
BetterAuth::Hanami::Generators::RelationGenerator.new.run
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
RUBY
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "time"
|
|
5
|
+
|
|
6
|
+
module BetterAuth
|
|
7
|
+
module Hanami
|
|
8
|
+
module Generators
|
|
9
|
+
class MigrationGenerator
|
|
10
|
+
def initialize(destination_root: Dir.pwd, configuration: nil)
|
|
11
|
+
@destination_root = destination_root
|
|
12
|
+
@configuration = configuration
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def run
|
|
16
|
+
return migration_path if existing_migration?
|
|
17
|
+
|
|
18
|
+
FileUtils.mkdir_p(File.dirname(migration_path))
|
|
19
|
+
File.write(migration_path, BetterAuth::Hanami::Migration.render(generator_config))
|
|
20
|
+
migration_path
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
attr_reader :destination_root, :configuration
|
|
26
|
+
|
|
27
|
+
def existing_migration?
|
|
28
|
+
Dir[File.join(destination_root, "config/db/migrate/*_create_better_auth_tables.rb")].any?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def migration_path
|
|
32
|
+
@migration_path ||= File.join(destination_root, "config/db/migrate", "#{timestamp}_create_better_auth_tables.rb")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def timestamp
|
|
36
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def generator_config
|
|
40
|
+
return configuration if configuration
|
|
41
|
+
|
|
42
|
+
options = BetterAuth::Hanami.configuration.to_auth_options
|
|
43
|
+
options[:secret] ||= BetterAuth::Configuration::DEFAULT_SECRET
|
|
44
|
+
options[:database] ||= :memory
|
|
45
|
+
BetterAuth::Configuration.new(options)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module BetterAuth
|
|
6
|
+
module Hanami
|
|
7
|
+
module Generators
|
|
8
|
+
class RelationGenerator
|
|
9
|
+
def initialize(destination_root: Dir.pwd, configuration: nil)
|
|
10
|
+
@destination_root = destination_root
|
|
11
|
+
@configuration = configuration
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run
|
|
15
|
+
create_base_repo
|
|
16
|
+
generator_config && tables.each_value do |table|
|
|
17
|
+
create_relation(table)
|
|
18
|
+
create_repo(table)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
attr_reader :destination_root, :configuration
|
|
25
|
+
|
|
26
|
+
def create_base_repo
|
|
27
|
+
path = File.join(destination_root, "app/repo.rb")
|
|
28
|
+
return if File.exist?(path)
|
|
29
|
+
|
|
30
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
31
|
+
File.write(path, base_repo_template)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def create_relation(table)
|
|
35
|
+
table_name = table.fetch(:model_name)
|
|
36
|
+
path = File.join(destination_root, "app/relations", "#{table_name}.rb")
|
|
37
|
+
return if File.exist?(path)
|
|
38
|
+
|
|
39
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
40
|
+
File.write(path, relation_template(table_name))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def create_repo(table)
|
|
44
|
+
table_name = table.fetch(:model_name)
|
|
45
|
+
path = File.join(destination_root, "app/repos", "#{singular_name(table_name)}_repo.rb")
|
|
46
|
+
return if File.exist?(path)
|
|
47
|
+
|
|
48
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
49
|
+
File.write(path, repo_template(table_name))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def tables
|
|
53
|
+
BetterAuth::Schema.auth_tables(generator_config)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def generator_config
|
|
57
|
+
@generator_config ||= begin
|
|
58
|
+
return configuration if configuration
|
|
59
|
+
|
|
60
|
+
options = BetterAuth::Hanami.configuration.to_auth_options
|
|
61
|
+
options[:secret] ||= BetterAuth::Configuration::DEFAULT_SECRET
|
|
62
|
+
options[:database] ||= :memory
|
|
63
|
+
BetterAuth::Configuration.new(options)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def app_namespace
|
|
68
|
+
@app_namespace ||= begin
|
|
69
|
+
candidates = [
|
|
70
|
+
File.join(destination_root, "config/app.rb"),
|
|
71
|
+
File.join(destination_root, "config/routes.rb"),
|
|
72
|
+
File.join(destination_root, "config/settings.rb")
|
|
73
|
+
]
|
|
74
|
+
candidates.filter_map do |path|
|
|
75
|
+
next unless File.exist?(path)
|
|
76
|
+
|
|
77
|
+
File.read(path).match(/module\s+([A-Z][A-Za-z0-9_:]*)/)&.[](1)
|
|
78
|
+
end.first || "Main"
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def base_repo_template
|
|
83
|
+
<<~RUBY
|
|
84
|
+
# frozen_string_literal: true
|
|
85
|
+
|
|
86
|
+
module #{app_namespace}
|
|
87
|
+
class Repo < Hanami::DB::Repo
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
RUBY
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def relation_template(table_name)
|
|
94
|
+
<<~RUBY
|
|
95
|
+
# frozen_string_literal: true
|
|
96
|
+
|
|
97
|
+
module #{app_namespace}
|
|
98
|
+
module Relations
|
|
99
|
+
class #{class_name(table_name)} < Hanami::DB::Relation
|
|
100
|
+
schema :#{table_name}, infer: true
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
RUBY
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def repo_template(table_name)
|
|
108
|
+
<<~RUBY
|
|
109
|
+
# frozen_string_literal: true
|
|
110
|
+
|
|
111
|
+
module #{app_namespace}
|
|
112
|
+
module Repos
|
|
113
|
+
class #{class_name(singular_name(table_name))}Repo < Repo[:#{table_name}]
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
RUBY
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def class_name(value)
|
|
121
|
+
value.to_s.split("_").map(&:capitalize).join
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def singular_name(value)
|
|
125
|
+
value.to_s.sub(/ies\z/, "y").sub(/s\z/, "")
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|