databasium 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 +32 -0
- data/MIT-LICENSE +20 -0
- data/README.md +109 -0
- data/Rakefile +6 -0
- data/app/assets/builds/application.js +9045 -0
- data/app/assets/builds/application.js.map +7 -0
- data/app/assets/builds/databasium.css +2 -0
- data/app/assets/config/databasium_manifest.js +1 -0
- data/app/assets/javascript/databasium/application.js +2 -0
- data/app/assets/javascript/databasium/controllers/attribute_controller.js +27 -0
- data/app/assets/javascript/databasium/controllers/collapse_controller.js +18 -0
- data/app/assets/javascript/databasium/controllers/error_controller.js +15 -0
- data/app/assets/javascript/databasium/controllers/filter_controller.js +224 -0
- data/app/assets/javascript/databasium/controllers/flash_controller.js +18 -0
- data/app/assets/javascript/databasium/controllers/graph_controller.js +193 -0
- data/app/assets/javascript/databasium/controllers/index.js +7 -0
- data/app/assets/javascript/databasium/controllers/layout_controller.js +13 -0
- data/app/assets/javascript/databasium/controllers/model_controller.js +32 -0
- data/app/assets/javascript/databasium/controllers/new_migration_controller.js +107 -0
- data/app/assets/javascript/databasium/controllers/relation_controller.js +10 -0
- data/app/assets/javascript/databasium/controllers/search_controller.js +23 -0
- data/app/assets/javascript/databasium/controllers/table_controller.js +283 -0
- data/app/assets/javascript/databasium/controllers/table_select_controller.js +19 -0
- data/app/assets/javascript/databasium/controllers/toggle_controller.js +28 -0
- data/app/assets/javascript/databasium/controllers/validation_controller.js +78 -0
- data/app/assets/javascript/databasium/shapes/erd_table_shape.js +54 -0
- data/app/assets/stylesheets/databasium/application.css +15 -0
- data/app/assets/stylesheets/databasium/colors.css +55 -0
- data/app/assets/stylesheets/databasium/custom.css +36 -0
- data/app/assets/stylesheets/databasium/databasium_engine.css +6 -0
- data/app/assets/stylesheets/databasium/pagy-tailwind.css +66 -0
- data/app/components/base.rb +50 -0
- data/app/components/databasium/collapsable.rb +62 -0
- data/app/components/databasium/forms/model.rb +147 -0
- data/app/components/databasium/forms/search.rb +31 -0
- data/app/components/databasium/global/error.rb +60 -0
- data/app/components/databasium/global/flash.rb +73 -0
- data/app/components/databasium/global/header_actions.rb +36 -0
- data/app/components/databasium/global/sidebar.rb +45 -0
- data/app/components/databasium/global/suggestion.rb +25 -0
- data/app/components/databasium/migrations/action.rb +39 -0
- data/app/components/databasium/migrations/file.rb +58 -0
- data/app/components/databasium/migrations/form.rb +222 -0
- data/app/components/databasium/migrations/header_actions.rb +87 -0
- data/app/components/databasium/migrations/migration_status.rb +22 -0
- data/app/components/databasium/migrations/preview.rb +29 -0
- data/app/components/databasium/migrations/show_turbo_stream.rb +19 -0
- data/app/components/databasium/migrations/sidebar.rb +28 -0
- data/app/components/databasium/models/attributes.rb +49 -0
- data/app/components/databasium/models/form.rb +100 -0
- data/app/components/databasium/models/header_actions.rb +51 -0
- data/app/components/databasium/models/model_preview.rb +31 -0
- data/app/components/databasium/models/sidebar.rb +25 -0
- data/app/components/databasium/models/templates/attribute.rb +99 -0
- data/app/components/databasium/models/templates/base.rb +6 -0
- data/app/components/databasium/models/templates/relation.rb +56 -0
- data/app/components/databasium/models/templates/validation.rb +285 -0
- data/app/components/databasium/navigation/base_icon.rb +32 -0
- data/app/components/databasium/navigation/frontend_icon.rb +17 -0
- data/app/components/databasium/navigation/get_icon.rb +26 -0
- data/app/components/databasium/navigation/icon.rb +28 -0
- data/app/components/databasium/navigation/icon_panel.rb +26 -0
- data/app/components/databasium/navigation/post_icon.rb +25 -0
- data/app/components/databasium/navigation/put_icon.rb +18 -0
- data/app/components/databasium/records/filter.rb +73 -0
- data/app/components/databasium/records/foreign_records.rb +84 -0
- data/app/components/databasium/records/header_actions.rb +110 -0
- data/app/components/databasium/records/show_turbo_stream.rb +75 -0
- data/app/components/databasium/records/sidebar.rb +23 -0
- data/app/components/databasium/records/table/record_panel.rb +60 -0
- data/app/components/databasium/records/table/row.rb +104 -0
- data/app/components/databasium/records/table.rb +125 -0
- data/app/components/databasium/records/table_turbo_frame.rb +37 -0
- data/app/components/databasium/records/utilities.rb +25 -0
- data/app/components/databasium/schemas/header_actions.rb +99 -0
- data/app/components/databasium/schemas/sidebar.rb +25 -0
- data/app/components/databasium/search_results/migrations.rb +37 -0
- data/app/components/databasium/search_results/models.rb +36 -0
- data/app/components/databasium/search_results/schema_models.rb +37 -0
- data/app/components/databasium/search_results/tables.rb +31 -0
- data/app/components/databasium/type_select.rb +35 -0
- data/app/controllers/databasium/application_controller.rb +68 -0
- data/app/controllers/databasium/homepage_controller.rb +5 -0
- data/app/controllers/databasium/migrations_controller.rb +186 -0
- data/app/controllers/databasium/models_controller.rb +105 -0
- data/app/controllers/databasium/records_controller.rb +156 -0
- data/app/controllers/databasium/schemas_controller.rb +52 -0
- data/app/helpers/databasium/application_helper.rb +4 -0
- data/app/helpers/databasium/heroicon_helper.rb +21 -0
- data/app/helpers/databasium/models_helper.rb +4 -0
- data/app/jobs/databasium/application_job.rb +4 -0
- data/app/mailers/databasium/application_mailer.rb +6 -0
- data/app/models/databasium/application_record.rb +5 -0
- data/app/models/model.json +0 -0
- data/app/services/databasium/migration.rb +176 -0
- data/app/services/databasium/model.rb +182 -0
- data/app/services/databasium/record.rb +65 -0
- data/app/services/databasium/schema.rb +146 -0
- data/app/views/base.rb +13 -0
- data/app/views/databasium/errors/non_development.rb +21 -0
- data/app/views/databasium/homepage/index.rb +29 -0
- data/app/views/databasium/migrations/index.rb +33 -0
- data/app/views/databasium/migrations/new.rb +29 -0
- data/app/views/databasium/models/index.rb +31 -0
- data/app/views/databasium/models/new.rb +37 -0
- data/app/views/databasium/records/index.rb +24 -0
- data/app/views/databasium/schemas/index.rb +39 -0
- data/app/views/layouts/databasium/application.rb +56 -0
- data/config/importmap.rb +12 -0
- data/config/initializers/heroicon.rb +12 -0
- data/config/initializers/pagy.rb +48 -0
- data/config/initializers/phlex.rb +19 -0
- data/config/routes.rb +31 -0
- data/config/tailwind.config.js +10 -0
- data/lib/databasium/engine.rb +57 -0
- data/lib/databasium/engine_mount.rb +37 -0
- data/lib/databasium/middleware/conditional_check_pending.rb +27 -0
- data/lib/databasium/templates/create_table_migration.rb.tt +29 -0
- data/lib/databasium/templates/migration.rb.tt +48 -0
- data/lib/databasium/templates/model.rb.tt +23 -0
- data/lib/databasium/version.rb +3 -0
- data/lib/databasium.rb +11 -0
- data/lib/tasks/databasium_tasks.rake +4 -0
- metadata +272 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Components
|
|
2
|
+
module Databasium
|
|
3
|
+
class Records::Utilities < Components::Base
|
|
4
|
+
def initialize(model:, columns_names_types:)
|
|
5
|
+
@model = model
|
|
6
|
+
@columns_names_types = columns_names_types
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def view_template
|
|
10
|
+
div(id: "records_utilities") do
|
|
11
|
+
render Components::Databasium::Records::Filter.new(
|
|
12
|
+
model: @model,
|
|
13
|
+
turbo_frame: "records",
|
|
14
|
+
columns_names_types: @columns_names_types,
|
|
15
|
+
hidden: true
|
|
16
|
+
)
|
|
17
|
+
render Components::Databasium::Forms::Model.new(
|
|
18
|
+
columns_names_types: @columns_names_types,
|
|
19
|
+
model: @model
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Components
|
|
4
|
+
module Databasium
|
|
5
|
+
class Schemas::HeaderActions < Components::Base
|
|
6
|
+
include Phlex::Rails::Helpers::LinkTo
|
|
7
|
+
include Phlex::Rails::Helpers::FormWith
|
|
8
|
+
include Phlex::Rails::Helpers::HiddenFieldTag
|
|
9
|
+
attr_reader :model, :layers
|
|
10
|
+
|
|
11
|
+
def initialize(model:, layers:)
|
|
12
|
+
@model = model
|
|
13
|
+
@layers = layers
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def view_template
|
|
17
|
+
div(id: "header_actions", class: "flex max-w-full gap-2 overflow-x-auto") do
|
|
18
|
+
unless model
|
|
19
|
+
render Components::Databasium::Navigation::IconPanel.new(
|
|
20
|
+
icons_with_text: [
|
|
21
|
+
{
|
|
22
|
+
icon: "arrow-path-rounded-square",
|
|
23
|
+
text: "Sync Schema",
|
|
24
|
+
method: :put,
|
|
25
|
+
path: databasium.sync_schema_schemas_path
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
if model
|
|
31
|
+
render Components::Databasium::Navigation::IconPanel.new(
|
|
32
|
+
icons_with_text: [
|
|
33
|
+
{
|
|
34
|
+
icon: "arrow-path-rounded-square",
|
|
35
|
+
text: "Sync Schema",
|
|
36
|
+
method: :put,
|
|
37
|
+
path: databasium.sync_schema_schemas_path
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
icon: "table-cells",
|
|
41
|
+
text: "Model Schema",
|
|
42
|
+
method: :get,
|
|
43
|
+
turbo_frame: "records",
|
|
44
|
+
path: model_layers_path(model, 0),
|
|
45
|
+
active: layers == 0
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
icon: "share",
|
|
49
|
+
text: "Model Associations",
|
|
50
|
+
method: :get,
|
|
51
|
+
turbo_frame: "records",
|
|
52
|
+
path: model_layers_path(model, 1),
|
|
53
|
+
active: layers == 1
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
icon: "square-2-stack",
|
|
57
|
+
text: "Nested associations",
|
|
58
|
+
method: :get,
|
|
59
|
+
turbo_frame: "records",
|
|
60
|
+
path: model_layers_path(model, 2),
|
|
61
|
+
active: layers == 2
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
icon: "squares-2x2",
|
|
65
|
+
text: "All associations",
|
|
66
|
+
method: :get,
|
|
67
|
+
turbo_frame: "records",
|
|
68
|
+
path: model_layers_path(model, nil),
|
|
69
|
+
active: layers == nil
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
if model
|
|
75
|
+
form_with(
|
|
76
|
+
method: :get,
|
|
77
|
+
url: model_layers_path(model, layers),
|
|
78
|
+
class: "border-l border-border pl-2 inline-flex gap-2 items-center"
|
|
79
|
+
) do |form|
|
|
80
|
+
hidden_field_tag :model, model
|
|
81
|
+
form.number_field :layers,
|
|
82
|
+
value: layers,
|
|
83
|
+
class:
|
|
84
|
+
"w-10 border-1 border-border rounded-xl p-1 text-center text-sm"
|
|
85
|
+
form.submit "Search for associations",
|
|
86
|
+
class: "text-sm bg-accent shadow-accent rounded-xl p-1.5 text-center"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def model_layers_path(model, layers)
|
|
95
|
+
databasium.schemas_path(model: model, layers: layers)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Components
|
|
4
|
+
module Databasium
|
|
5
|
+
class Schemas::Sidebar < Components::Base
|
|
6
|
+
include Phlex::Rails::Helpers::TurboFrameTag
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def view_template
|
|
12
|
+
div(class: "flex flex-col gap-2", data: { controller: "search" }) do
|
|
13
|
+
render Components::Databasium::Forms::Search.new(
|
|
14
|
+
url: databasium.sidebar_schemas_path,
|
|
15
|
+
turbo_frame: "results",
|
|
16
|
+
placeholder: "Search for a model"
|
|
17
|
+
)
|
|
18
|
+
turbo_frame_tag("results", src: databasium.sidebar_schemas_path) do
|
|
19
|
+
p(class: "mt-2 animate-pulse") { "Loading models..." }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Components
|
|
4
|
+
module Databasium
|
|
5
|
+
class SearchResults::Migrations < Components::Base
|
|
6
|
+
include Phlex::Rails::Helpers::LinkTo
|
|
7
|
+
include Phlex::Rails::Helpers::TurboFrameTag
|
|
8
|
+
|
|
9
|
+
def initialize(migrations:, pending_migrations:, pagy:)
|
|
10
|
+
@migrations = migrations
|
|
11
|
+
@pending_migrations = pending_migrations
|
|
12
|
+
@pagy = pagy
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def view_template
|
|
16
|
+
turbo_frame_tag("results") do
|
|
17
|
+
@migrations.each do |m|
|
|
18
|
+
status = @pending_migrations.include?(m.version) ? "pending" : "applied"
|
|
19
|
+
link_to(
|
|
20
|
+
databasium.migration_path(m.version),
|
|
21
|
+
data: {
|
|
22
|
+
turbo_stream: true
|
|
23
|
+
},
|
|
24
|
+
class:
|
|
25
|
+
"text-main-text hover:text-hover hover:cursor-pointer flex items-center gap-2 p-1 border-b
|
|
26
|
+
border-border flex items-center justify-between"
|
|
27
|
+
) do
|
|
28
|
+
p(class: "max-w-fit overflow-x-auto me-2 scrollbar-thin p-1") { "#{m.name}" }
|
|
29
|
+
render Migrations::MigrationStatus.new(status: status, version: m.version)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
div(class: "mt-4 flex justify-start") { raw @pagy.series_nav.html_safe } if @pagy
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Components
|
|
4
|
+
module Databasium
|
|
5
|
+
class SearchResults::Models < Components::Base
|
|
6
|
+
include Phlex::Rails::Helpers::LinkTo
|
|
7
|
+
include Phlex::Rails::Helpers::TurboFrameTag
|
|
8
|
+
|
|
9
|
+
def initialize(models:, pagy:)
|
|
10
|
+
@models = models
|
|
11
|
+
@pagy = pagy
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def view_template
|
|
15
|
+
turbo_frame_tag("results") do
|
|
16
|
+
@models&.each do |model|
|
|
17
|
+
link_to(
|
|
18
|
+
databasium.model_path(model),
|
|
19
|
+
data: {
|
|
20
|
+
turbo_frame: "main"
|
|
21
|
+
},
|
|
22
|
+
class:
|
|
23
|
+
"text-main-text hover:text-hover hover:cursor-pointer flex items-center gap-2 p-1 border-b
|
|
24
|
+
border-border flex items-center justify-between"
|
|
25
|
+
) do
|
|
26
|
+
p(class: "max-w-fit overflow-x-auto me-2 scrollbar-thin p-1") do
|
|
27
|
+
"#{model.upcase_first}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
div(class: "mt-4 flex justify-start") { raw @pagy.series_nav.html_safe } if @pagy
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Components
|
|
4
|
+
module Databasium
|
|
5
|
+
class SearchResults::SchemaModels < Components::Base
|
|
6
|
+
include Phlex::Rails::Helpers::LinkTo
|
|
7
|
+
include Phlex::Rails::Helpers::TurboFrameTag
|
|
8
|
+
|
|
9
|
+
def initialize(models:, pagy:)
|
|
10
|
+
@models = models
|
|
11
|
+
@pagy = pagy
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def view_template
|
|
15
|
+
turbo_frame_tag("results") do
|
|
16
|
+
@models&.each do |model|
|
|
17
|
+
link_to(
|
|
18
|
+
databasium.schemas_path(model: model.upcase_first, layers: 0),
|
|
19
|
+
data: {
|
|
20
|
+
turbo_frame: "main"
|
|
21
|
+
},
|
|
22
|
+
class:
|
|
23
|
+
"text-main-text hover:text-hover hover:cursor-pointer flex items-center gap-2 p-1 border-b
|
|
24
|
+
border-border justify-between"
|
|
25
|
+
) do
|
|
26
|
+
p(class: "max-w-fit overflow-x-auto me-2 scrollbar-thin p-1") do
|
|
27
|
+
"#{model.upcase_first}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
p(class: "text-main-text text-center p-4") { "No models found" } if @models.empty?
|
|
32
|
+
div(class: "mt-4 flex justify-start") { raw @pagy.series_nav.html_safe } if @pagy
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Components
|
|
4
|
+
module Databasium
|
|
5
|
+
class SearchResults::Tables < Components::Base
|
|
6
|
+
include Phlex::Rails::Helpers::LinkTo
|
|
7
|
+
include Phlex::Rails::Helpers::TurboFrameTag
|
|
8
|
+
|
|
9
|
+
def initialize(tables:, pagy:)
|
|
10
|
+
@tables = tables
|
|
11
|
+
@pagy = pagy
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def view_template
|
|
15
|
+
turbo_frame_tag("results") do
|
|
16
|
+
@tables&.each do |table|
|
|
17
|
+
div(class: "border-b-2 border-b-border py-2 px-3") do
|
|
18
|
+
link_to "#{table}",
|
|
19
|
+
databasium.records_records_path(table: table, refresh: true),
|
|
20
|
+
data: {
|
|
21
|
+
turbo_frame: "_top",
|
|
22
|
+
turbo_stream: true
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
div(class: "mt-4 flex justify-start") { raw @pagy.series_nav.html_safe } if @pagy
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Components
|
|
4
|
+
module Databasium
|
|
5
|
+
class TypeSelect < Components::Base
|
|
6
|
+
def initialize(name: nil, value: nil)
|
|
7
|
+
@name = name
|
|
8
|
+
@value = value
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def view_template
|
|
12
|
+
select(
|
|
13
|
+
name: @name,
|
|
14
|
+
class:
|
|
15
|
+
"border-2 rounded-xl px-2 py-1 border-border h-full bg-background focus:outline-none"
|
|
16
|
+
) do
|
|
17
|
+
[
|
|
18
|
+
%w[text Text],
|
|
19
|
+
%w[string String],
|
|
20
|
+
%w[integer Integer],
|
|
21
|
+
%w[float Float],
|
|
22
|
+
%w[decimal Decimal],
|
|
23
|
+
%w[time Time],
|
|
24
|
+
%w[date Date],
|
|
25
|
+
%w[datetime Datetime],
|
|
26
|
+
%w[timestamp Timestamp],
|
|
27
|
+
%w[binary Binary],
|
|
28
|
+
%w[boolean Boolean],
|
|
29
|
+
%w[references Reference]
|
|
30
|
+
].each { |value, label| option(value: value, selected: @value == value) { label } }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Databasium
|
|
2
|
+
class ApplicationController < ActionController::Base
|
|
3
|
+
helper ::Databasium::HeroiconHelper
|
|
4
|
+
|
|
5
|
+
layout -> { Views::Layouts::Databasium::Application.new }
|
|
6
|
+
before_action :check_development_environment
|
|
7
|
+
|
|
8
|
+
rescue_from Exception, with: :render_error_flash if Rails.env.development?
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def check_development_environment
|
|
13
|
+
render Views::Databasium::Errors::NonDevelopment.new if Rails.env.production?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def render_error_flash(error)
|
|
17
|
+
Rails.logger.error("[Databasium] #{error.class}: #{error.message}")
|
|
18
|
+
Rails.logger.error(error.backtrace.join("\n")) if error.backtrace
|
|
19
|
+
|
|
20
|
+
return if performed?
|
|
21
|
+
|
|
22
|
+
message, details = error_message_parts(error)
|
|
23
|
+
|
|
24
|
+
respond_to do |format|
|
|
25
|
+
format.turbo_stream do
|
|
26
|
+
render turbo_stream: [
|
|
27
|
+
turbo_stream.replace(
|
|
28
|
+
"flash",
|
|
29
|
+
Components::Databasium::Global::Flash.new(
|
|
30
|
+
success: flash[:success],
|
|
31
|
+
error: flash[:error]
|
|
32
|
+
)
|
|
33
|
+
),
|
|
34
|
+
turbo_stream.replace(
|
|
35
|
+
"error",
|
|
36
|
+
Components::Databasium::Global::Error.new(
|
|
37
|
+
message: message,
|
|
38
|
+
details: details,
|
|
39
|
+
type: error.class.name
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
],
|
|
43
|
+
status: :internal_server_error
|
|
44
|
+
end
|
|
45
|
+
format.any do
|
|
46
|
+
render Components::Databasium::Global::Error.new(
|
|
47
|
+
message: message,
|
|
48
|
+
details: details,
|
|
49
|
+
type: error.class.name
|
|
50
|
+
),
|
|
51
|
+
status: :internal_server_error
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def error_message_parts(error)
|
|
57
|
+
lines = strip_ansi(error.message.to_s).lines.map(&:chomp)
|
|
58
|
+
message = lines.shift.presence || "Something went wrong"
|
|
59
|
+
details = lines.join("\n")
|
|
60
|
+
|
|
61
|
+
[ message, details.presence || strip_ansi(error.backtrace&.join("\n").to_s).presence ]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def strip_ansi(text)
|
|
65
|
+
text.gsub(/\e\[[0-9;]*m/, "")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
class Databasium::MigrationsController < Databasium::ApplicationController
|
|
2
|
+
before_action :set_migration_service, except: [ :index ]
|
|
3
|
+
after_action -> { Databasium::Schema.new.sync! },
|
|
4
|
+
only: %i[run_migration rollback_migration run_pending_migrations]
|
|
5
|
+
include Pagy::Method
|
|
6
|
+
|
|
7
|
+
def index
|
|
8
|
+
render Views::Databasium::Migrations::Index.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def show
|
|
12
|
+
@migration = @migration_service.find_migration!(params[:id])
|
|
13
|
+
@content = File.read(@migration.filename)
|
|
14
|
+
|
|
15
|
+
respond_to do |format|
|
|
16
|
+
format.html do
|
|
17
|
+
render Components::Databasium::Migrations::File.new(
|
|
18
|
+
migration: @migration,
|
|
19
|
+
content: @content
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
format.turbo_stream do
|
|
23
|
+
render Components::Databasium::Migrations::ShowTurboStream.new(
|
|
24
|
+
migration: @migration,
|
|
25
|
+
content: @content
|
|
26
|
+
),
|
|
27
|
+
layout: false
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def new
|
|
33
|
+
@tables = Databasium::Schema.new.tables
|
|
34
|
+
render Views::Databasium::Migrations::New.new(tables: @tables)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def create
|
|
38
|
+
require "rails/generators"
|
|
39
|
+
Rails.application.load_generators
|
|
40
|
+
require "rails/generators/active_record/migration/migration_generator"
|
|
41
|
+
|
|
42
|
+
if params[:add_migration] == "Save"
|
|
43
|
+
success = @migration_service.save_migration(migration_params)
|
|
44
|
+
else
|
|
45
|
+
content = @migration_service.generate_migration(migration_params)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
if success
|
|
49
|
+
flash[:success] = "Migration for table #{migration_params[:table_name]} saved successfully."
|
|
50
|
+
redirect_to migrations_path, status: :see_other
|
|
51
|
+
elsif content
|
|
52
|
+
render turbo_stream:
|
|
53
|
+
turbo_stream.replace(
|
|
54
|
+
"migration_preview",
|
|
55
|
+
Components::Databasium::Migrations::Preview.new(content: content)
|
|
56
|
+
)
|
|
57
|
+
else
|
|
58
|
+
head :unprocessable_entity
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def sidebar
|
|
63
|
+
pagy, migrations =
|
|
64
|
+
pagy(@migration_service.get_migrations(params[:search]), limit: 5, root_key: "migrations")
|
|
65
|
+
pending_migrations = @migration_service.pending_migrations
|
|
66
|
+
|
|
67
|
+
render Components::Databasium::SearchResults::Migrations.new(
|
|
68
|
+
migrations: migrations,
|
|
69
|
+
pending_migrations: pending_migrations,
|
|
70
|
+
pagy: pagy
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def run_pending_migrations
|
|
75
|
+
versions = @migration_service.run_pending_migrations
|
|
76
|
+
message =
|
|
77
|
+
if versions.any?
|
|
78
|
+
"Pending migrations(#{versions.count}) run successfully"
|
|
79
|
+
else
|
|
80
|
+
"No pending migrations to run"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
respond_to do |format|
|
|
84
|
+
format.html { redirect_to migrations_path, notice: message }
|
|
85
|
+
format.turbo_stream do
|
|
86
|
+
streams = [
|
|
87
|
+
turbo_stream.replace("error", Components::Databasium::Global::Error.new),
|
|
88
|
+
turbo_stream.replace(
|
|
89
|
+
"flash",
|
|
90
|
+
Components::Databasium::Global::Flash.new(success: message, error: nil)
|
|
91
|
+
)
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
streams +=
|
|
95
|
+
versions.map do |version|
|
|
96
|
+
turbo_stream.replace(
|
|
97
|
+
"migration_#{version}_status",
|
|
98
|
+
Components::Databasium::Migrations::MigrationStatus.new(
|
|
99
|
+
status: "applied",
|
|
100
|
+
version: version
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
render turbo_stream: streams
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def rollback_migration
|
|
111
|
+
version = rollback_migration_params[:version]
|
|
112
|
+
success =
|
|
113
|
+
@migration_service.rollback_migration(
|
|
114
|
+
version,
|
|
115
|
+
rollback_migration_params[:rollback_steps],
|
|
116
|
+
rollback_migration_params[:till_this_migration]
|
|
117
|
+
)
|
|
118
|
+
message = "Migration rolled back successfully"
|
|
119
|
+
if rollback_migration_params[:till_this_migration] == "true" ||
|
|
120
|
+
rollback_migration_params[:rollback_steps].present?
|
|
121
|
+
set_action_flash(message, nil)
|
|
122
|
+
redirect_to migrations_path(version: version)
|
|
123
|
+
else
|
|
124
|
+
response_to_action(message, nil, version, success ? "pending" : nil)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def run_migration
|
|
129
|
+
version = run_migration_params[:version]
|
|
130
|
+
@migration_service.run_migration(version)
|
|
131
|
+
response_to_action("Migration run successfully", nil, version, "applied")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
private
|
|
135
|
+
|
|
136
|
+
def set_migration_service
|
|
137
|
+
@migration_service = Databasium::Migration.new
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def migration_params
|
|
141
|
+
params.permit(
|
|
142
|
+
:add_migration,
|
|
143
|
+
:migration_action,
|
|
144
|
+
:table_name,
|
|
145
|
+
:table_name_from,
|
|
146
|
+
:table_name_to,
|
|
147
|
+
:add_model,
|
|
148
|
+
columns: %i[column_name column_type],
|
|
149
|
+
validation: %i[column_name type]
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def run_migration_params
|
|
154
|
+
params.permit(:version)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def rollback_migration_params
|
|
158
|
+
params.permit(:version, :till_this_migration, :rollback_steps)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def new_action_response(success, error, migration_version, status)
|
|
162
|
+
Components::Databasium::Migrations::Action.new(
|
|
163
|
+
success: success,
|
|
164
|
+
error: error,
|
|
165
|
+
migration_version: migration_version,
|
|
166
|
+
status: status
|
|
167
|
+
)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def response_to_action(success, error, migration_version, status)
|
|
171
|
+
respond_to do |format|
|
|
172
|
+
format.turbo_stream do
|
|
173
|
+
render new_action_response(success, error, migration_version, status), layout: false
|
|
174
|
+
end
|
|
175
|
+
format.html do
|
|
176
|
+
set_action_flash(success, error)
|
|
177
|
+
redirect_to migrations_path(version: migration_version)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def set_action_flash(success, error)
|
|
183
|
+
flash[:success] = success if success.present?
|
|
184
|
+
flash[:error] = error if error.present?
|
|
185
|
+
end
|
|
186
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
class Databasium::ModelsController < Databasium::ApplicationController
|
|
2
|
+
include Pagy::Method
|
|
3
|
+
before_action :create_model_service
|
|
4
|
+
MODEL_TEMPLATE_PATH = Databasium::Engine.root.join("lib/databasium/templates/model.rb.tt")
|
|
5
|
+
|
|
6
|
+
def new
|
|
7
|
+
models = @model_service.get_all_models_from_db(search: params[:search])
|
|
8
|
+
|
|
9
|
+
render Views::Databasium::Models::New.new(content: nil, models: models)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def show
|
|
13
|
+
model = params[:id]
|
|
14
|
+
content = @model_service.read_model_file(model)
|
|
15
|
+
attributes = @model_service.get_model_data_from_file(model)
|
|
16
|
+
models = @model_service.get_all_models_from_db(search: params[:search])
|
|
17
|
+
|
|
18
|
+
respond_to do |format|
|
|
19
|
+
format.html do
|
|
20
|
+
render Views::Databasium::Models::New.new(
|
|
21
|
+
content: content,
|
|
22
|
+
model: model,
|
|
23
|
+
attributes: attributes,
|
|
24
|
+
models: models
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
format.turbo_stream do
|
|
28
|
+
render turbo_stream:
|
|
29
|
+
turbo_stream.replace(
|
|
30
|
+
"model_preview",
|
|
31
|
+
Components::Databasium::Models::ModelPreview.new(content: content)
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def sidebar
|
|
38
|
+
models = @model_service.get_all_models_from_db(search: params[:search])
|
|
39
|
+
pagy, models = pagy(models, limit: 7, root_key: "models")
|
|
40
|
+
|
|
41
|
+
render Components::Databasium::SearchResults::Models.new(models: models, pagy: pagy)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def create
|
|
45
|
+
content = generate_model_content
|
|
46
|
+
if params[:commit] == "Create model file"
|
|
47
|
+
if write_file(content)
|
|
48
|
+
render turbo_stream:
|
|
49
|
+
turbo_stream.replace(
|
|
50
|
+
"flash",
|
|
51
|
+
Components::Databasium::Global::Flash.new(
|
|
52
|
+
success:
|
|
53
|
+
"Model file created successfully, be sure to create a migration for this model if you haven't already"
|
|
54
|
+
)
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
respond_to do |format|
|
|
59
|
+
format.html
|
|
60
|
+
format.turbo_stream do
|
|
61
|
+
render turbo_stream:
|
|
62
|
+
turbo_stream.replace(
|
|
63
|
+
"model_preview",
|
|
64
|
+
Components::Databasium::Models::ModelPreview.new(content: content)
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def create_model_service
|
|
74
|
+
@model_service = Databasium::Model.new
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def generate_model_content
|
|
78
|
+
renderer = ERB.new(File.read(MODEL_TEMPLATE_PATH), trim_mode: "-")
|
|
79
|
+
|
|
80
|
+
context =
|
|
81
|
+
@model_service.create_model_data(
|
|
82
|
+
model_name: model_params[:model_name],
|
|
83
|
+
attributes: model_params[:attributes],
|
|
84
|
+
relations: model_params[:relations],
|
|
85
|
+
unknown: model_params[:unknown]
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
renderer.result(context.get_binding)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def write_file(content)
|
|
92
|
+
model_name = model_params[:model_name].underscore
|
|
93
|
+
destination_path = Rails.root.join("app/models/#{model_name}.rb")
|
|
94
|
+
File.open(destination_path, "w") { |file| file.write(content) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def model_params
|
|
98
|
+
params.require(:model).permit(
|
|
99
|
+
:model_name,
|
|
100
|
+
attributes: [ :name, :type, validations: %i[name type value] ],
|
|
101
|
+
relations: %i[type table_name],
|
|
102
|
+
unknown: []
|
|
103
|
+
)
|
|
104
|
+
end
|
|
105
|
+
end
|