kadim 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +13 -1
- data/app/assets/javascripts/kadim/application.js +1 -1
- data/app/assets/javascripts/kadim/bulma/application.js +22 -0
- data/app/assets/javascripts/kadim/bulma/direct_upload.js +36 -0
- data/app/assets/javascripts/kadim/bulma/resumable_upload.js +36 -0
- data/app/assets/stylesheets/kadim/application.css +1 -1
- data/app/assets/stylesheets/kadim/bulma/application.scss +151 -0
- data/app/assets/stylesheets/kadim/bulma/direct_upload.css +19 -0
- data/app/assets/stylesheets/kadim/bulma/resumable_upload.css +19 -0
- data/app/controllers/kadim/application_controller.rb +5 -0
- data/app/helpers/kadim/application_helper.rb +15 -3
- data/app/views/kadim/application/_notice.html.erb +6 -0
- data/app/views/layouts/kadim/bulma/application.html.erb +67 -0
- data/lib/generators/kadim/scaffold_controller/scaffold_controller_generator.rb +2 -0
- data/lib/generators/kadim/scaffold_controller/templates/bulma/_form.html.erb.tt +112 -0
- data/lib/generators/kadim/scaffold_controller/templates/bulma/edit.html.erb.tt +5 -0
- data/lib/generators/kadim/scaffold_controller/templates/bulma/index.html.erb.tt +49 -0
- data/lib/generators/kadim/scaffold_controller/templates/bulma/new.html.erb.tt +5 -0
- data/lib/generators/kadim/scaffold_controller/templates/bulma/show.html.erb.tt +32 -0
- data/lib/kadim.rb +17 -6
- data/lib/kadim/engine.rb +5 -0
- data/lib/kadim/version.rb +1 -1
- metadata +87 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 969b2f040793f3267751ee1fcb3a66ca7fa7378858a21259a1ac882b68fec8c8
|
4
|
+
data.tar.gz: 05e6d286c7780728b386de66614001b4f530be45a3b83e808637262d43091f63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7be33523c6f406d972622515396d83145f45287afa7acb5c3d0e74e48517d167ae44594f667216956c934af0d86fcbe60c1c2a2f45bb75f54b6cbfdabf407a11
|
7
|
+
data.tar.gz: f30402ebcd1bf3f1cfb43ed37da84ae4080972450eb29d81bf6e9ed484bf117de2c3453012d758523a05d418033899f835d87b0f1e28e5851576e07575a0c987
|
data/README.md
CHANGED
@@ -17,6 +17,18 @@ allows you to run kadim in environments with ephemeral file systems, like [herok
|
|
17
17
|
|
18
18
|
Just follow the [Installation](#installation) section and access http://localhost:3000/kadim
|
19
19
|
|
20
|
+
### [bulma.io](https://bulma.io) layout
|
21
|
+
If you want a more beautiful view, add the following to your configuration file:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
# config/initializers/kadim.rb
|
25
|
+
Kadim.configure do |config|
|
26
|
+
config.layout = :bulma
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
![kadim with bulma](kadim.png)
|
31
|
+
|
20
32
|
### ActiveStorage support
|
21
33
|
If we detect that you have S3, GCS or Azure Storage configured we will use
|
22
34
|
[Direct Uploads](https://edgeguides.rubyonrails.org/active_storage_overview.html#direct-uploads) by default. If you are
|
@@ -95,7 +107,7 @@ And access http://localhost:3000/kadim
|
|
95
107
|
- [x] Tasks to copy files form kadim to the hosted application
|
96
108
|
- [x] Add support to ActiveStorage attachments
|
97
109
|
- [ ] Add support to belongs_to relationships
|
98
|
-
- [
|
110
|
+
- [x] Add a beautiful look and feel
|
99
111
|
|
100
112
|
## License
|
101
113
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,22 @@
|
|
1
|
+
//= require rails-ujs
|
2
|
+
//= require activestorage
|
3
|
+
//= require activestorage-resumable
|
4
|
+
//= require_tree .
|
5
|
+
|
6
|
+
document.addEventListener('DOMContentLoaded', () => {
|
7
|
+
(document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
|
8
|
+
const $notification = $delete.parentNode
|
9
|
+
$delete.addEventListener('click', () => {
|
10
|
+
$notification.parentNode.removeChild($notification)
|
11
|
+
})
|
12
|
+
})
|
13
|
+
});
|
14
|
+
|
15
|
+
(function () {
|
16
|
+
var burger = document.querySelector('.burger')
|
17
|
+
var menu = document.querySelector('#' + burger.dataset.target)
|
18
|
+
burger.addEventListener('click', function () {
|
19
|
+
burger.classList.toggle('is-active')
|
20
|
+
menu.classList.toggle('is-active')
|
21
|
+
})
|
22
|
+
})()
|
@@ -0,0 +1,36 @@
|
|
1
|
+
addEventListener('direct-upload:initialize', event => {
|
2
|
+
const { target, detail } = event
|
3
|
+
const { id, file } = detail
|
4
|
+
target.insertAdjacentHTML('beforebegin', `
|
5
|
+
<div id="direct-upload-${id}" class="direct-upload direct-upload--pending">
|
6
|
+
<span>${file.name}</span>
|
7
|
+
<progress id="direct-upload-progress-${id}" class="progress" value="0" max="100"></progress>
|
8
|
+
</div>
|
9
|
+
`)
|
10
|
+
})
|
11
|
+
|
12
|
+
addEventListener('direct-upload:start', event => {
|
13
|
+
const { id } = event.detail
|
14
|
+
const element = document.getElementById(`direct-upload-${id}`)
|
15
|
+
element.classList.remove('direct-upload--pending')
|
16
|
+
})
|
17
|
+
|
18
|
+
addEventListener('direct-upload:progress', event => {
|
19
|
+
const { id, progress } = event.detail
|
20
|
+
const progressElement = document.getElementById(`direct-upload-progress-${id}`)
|
21
|
+
progressElement.value = Math.round(progress)
|
22
|
+
})
|
23
|
+
|
24
|
+
addEventListener('direct-upload:error', event => {
|
25
|
+
event.preventDefault()
|
26
|
+
const { id, error } = event.detail
|
27
|
+
const element = document.getElementById(`direct-upload-${id}`)
|
28
|
+
element.classList.add('direct-upload--error')
|
29
|
+
element.setAttribute('title', error)
|
30
|
+
})
|
31
|
+
|
32
|
+
addEventListener('direct-upload:end', event => {
|
33
|
+
const { id } = event.detail
|
34
|
+
const element = document.getElementById(`direct-upload-${id}`)
|
35
|
+
element.classList.add('direct-upload--complete')
|
36
|
+
})
|
@@ -0,0 +1,36 @@
|
|
1
|
+
addEventListener('resumable-upload:initialize', event => {
|
2
|
+
const { target, detail } = event
|
3
|
+
const { id, file } = detail
|
4
|
+
target.parentNode.parentNode.insertAdjacentHTML('beforebegin', `
|
5
|
+
<div id="resumable-upload-${id}" class="resumable-upload resumable-upload--pending">
|
6
|
+
<span>${file.name}</span>
|
7
|
+
<progress id="resumable-upload-progress-${id}" class="progress" value="0" max="100"></progress>
|
8
|
+
</div>
|
9
|
+
`)
|
10
|
+
})
|
11
|
+
|
12
|
+
addEventListener('resumable-upload:start', event => {
|
13
|
+
const { id } = event.detail
|
14
|
+
const element = document.getElementById(`resumable-upload-${id}`)
|
15
|
+
element.classList.remove('resumable-upload--pending')
|
16
|
+
})
|
17
|
+
|
18
|
+
addEventListener('resumable-upload:progress', event => {
|
19
|
+
const { id, progress } = event.detail
|
20
|
+
const progressElement = document.getElementById(`resumable-upload-progress-${id}`)
|
21
|
+
progressElement.value = Math.round(progress)
|
22
|
+
})
|
23
|
+
|
24
|
+
addEventListener('resumable-upload:error', event => {
|
25
|
+
event.preventDefault()
|
26
|
+
const { id, error } = event.detail
|
27
|
+
const element = document.getElementById(`resumable-upload-${id}`)
|
28
|
+
element.classList.add('resumable-upload--error')
|
29
|
+
element.setAttribute('title', error)
|
30
|
+
})
|
31
|
+
|
32
|
+
addEventListener('resumable-upload:end', event => {
|
33
|
+
const { id } = event.detail
|
34
|
+
const element = document.getElementById(`resumable-upload-${id}`)
|
35
|
+
element.classList.add('resumable-upload--complete')
|
36
|
+
})
|
@@ -0,0 +1,151 @@
|
|
1
|
+
$primary: hsl(217, 71%, 53%);
|
2
|
+
$menu-item-color: #adb5bd;
|
3
|
+
$menu-item-hover-color: #adb5bd;
|
4
|
+
$menu-item-hover-background-color: #495057;
|
5
|
+
$menu-item-active-background-color: #495057;
|
6
|
+
@import "bulma";
|
7
|
+
@import "font-awesome-sprockets";
|
8
|
+
@import "font-awesome";
|
9
|
+
|
10
|
+
@import "direct_upload";
|
11
|
+
@import "resumable_upload";
|
12
|
+
|
13
|
+
.brand-text {
|
14
|
+
font-family: 'Muli', sans-serif;
|
15
|
+
}
|
16
|
+
|
17
|
+
.menu-container {
|
18
|
+
min-height: 100vh;
|
19
|
+
flex-direction: column;
|
20
|
+
|
21
|
+
.brand-container {
|
22
|
+
border-bottom: 1px solid grey;
|
23
|
+
}
|
24
|
+
|
25
|
+
.models-container {
|
26
|
+
flex-grow: 1;
|
27
|
+
overflow: auto;
|
28
|
+
|
29
|
+
.models-container--column {
|
30
|
+
overflow: auto
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
table.table td.action {
|
36
|
+
background-clip: padding-box;
|
37
|
+
background-color: $white;
|
38
|
+
position: sticky;
|
39
|
+
right: 0;
|
40
|
+
white-space: nowrap;
|
41
|
+
width: 1px;
|
42
|
+
|
43
|
+
a {
|
44
|
+
padding: 0 0.375em;
|
45
|
+
&:first-child {
|
46
|
+
padding-left: 0;
|
47
|
+
}
|
48
|
+
&:last-child {
|
49
|
+
padding-right: 0;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
.table.is-striped tbody tr:not(.is-selected):nth-child(2n) {
|
55
|
+
td.action {
|
56
|
+
background-color: $white-bis;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
@media screen and (min-width: 1024px) {
|
61
|
+
aside.aside.is-expanded {
|
62
|
+
width: 14rem; }
|
63
|
+
aside.aside.is-expanded .menu-list .icon {
|
64
|
+
width: 3rem; }
|
65
|
+
aside.aside.is-expanded .menu-list .icon.has-update-mark:after {
|
66
|
+
right: 0.65rem; }
|
67
|
+
aside.aside.is-expanded .menu-list span.menu-item-label {
|
68
|
+
display: inline-block; }
|
69
|
+
aside.aside.is-expanded .menu-list li.is-active ul {
|
70
|
+
display: block; } }
|
71
|
+
|
72
|
+
aside.aside {
|
73
|
+
display: none;
|
74
|
+
position: fixed;
|
75
|
+
top: 0;
|
76
|
+
left: 0;
|
77
|
+
z-index: 40;
|
78
|
+
height: 100vh;
|
79
|
+
padding: 0;
|
80
|
+
box-shadow: none;
|
81
|
+
background: #2e323a; }
|
82
|
+
aside.aside .aside-tools {
|
83
|
+
display: flex;
|
84
|
+
flex-direction: row;
|
85
|
+
width: 100%;
|
86
|
+
background-color: #17191e;
|
87
|
+
color: white;
|
88
|
+
line-height: 3.25rem;
|
89
|
+
height: 3.25rem;
|
90
|
+
padding-left: 0.75rem;
|
91
|
+
flex: 1; }
|
92
|
+
aside.aside .aside-tools .icon {
|
93
|
+
margin-right: 0.75rem; }
|
94
|
+
aside.aside .menu-list li a.has-dropdown-icon {
|
95
|
+
position: relative;
|
96
|
+
padding-right: 3rem; }
|
97
|
+
aside.aside .menu-list li a.has-dropdown-icon .dropdown-icon {
|
98
|
+
position: absolute;
|
99
|
+
top: 0.5rem;
|
100
|
+
right: 0; }
|
101
|
+
aside.aside .menu-list li ul {
|
102
|
+
display: none;
|
103
|
+
border-left: 0;
|
104
|
+
background-color: #282c33;
|
105
|
+
padding-left: 0;
|
106
|
+
margin: 0 0 0.75rem; }
|
107
|
+
aside.aside .menu-list li ul li a {
|
108
|
+
padding: 0.75rem 0 0.75rem 0.75rem;
|
109
|
+
font-size: 0.95rem; }
|
110
|
+
aside.aside .menu-list li ul li a.has-icon {
|
111
|
+
padding-left: 0; }
|
112
|
+
aside.aside .menu-list li ul li a.is-active:not(:hover) {
|
113
|
+
background: transparent; }
|
114
|
+
aside.aside .menu-label {
|
115
|
+
padding: 0 0.75rem;
|
116
|
+
margin-top: 0.75rem;
|
117
|
+
margin-bottom: 0.75rem; }
|
118
|
+
|
119
|
+
@media screen and (max-width: 1023px) {
|
120
|
+
aside.aside {
|
121
|
+
transition: left 250ms ease-in-out 50ms; }
|
122
|
+
html.has-aside-mobile-transition body {
|
123
|
+
overflow-x: hidden; }
|
124
|
+
html.has-aside-mobile-transition body, html.has-aside-mobile-transition #app, html.has-aside-mobile-transition nav.navbar {
|
125
|
+
width: 100vw; }
|
126
|
+
html.has-aside-mobile-transition aside.aside {
|
127
|
+
width: 15rem;
|
128
|
+
display: block;
|
129
|
+
left: -15rem; }
|
130
|
+
html.has-aside-mobile-transition aside.aside .image img {
|
131
|
+
max-width: 4.95rem; }
|
132
|
+
html.has-aside-mobile-transition aside.aside .menu-list li.is-active ul {
|
133
|
+
display: block; }
|
134
|
+
html.has-aside-mobile-transition aside.aside .menu-list a .icon {
|
135
|
+
width: 3rem; }
|
136
|
+
html.has-aside-mobile-transition aside.aside .menu-list a .icon.has-update-mark:after {
|
137
|
+
right: 0.65rem; }
|
138
|
+
html.has-aside-mobile-transition aside.aside .menu-list a span.menu-item-label {
|
139
|
+
display: inline-block; }
|
140
|
+
html.has-aside-mobile-expanded #app, html.has-aside-mobile-expanded nav.navbar {
|
141
|
+
margin-left: 15rem; }
|
142
|
+
html.has-aside-mobile-expanded aside.aside {
|
143
|
+
left: 0; } }
|
144
|
+
|
145
|
+
aside.aside .menu-list li a.has-icon {
|
146
|
+
display: flex; }
|
147
|
+
aside.aside .menu-list li a.has-icon .icon i {
|
148
|
+
height: auto; }
|
149
|
+
aside.aside .menu-list li a.has-icon .menu-item-label {
|
150
|
+
line-height: 1.5rem;
|
151
|
+
height: 1.5rem; }
|
@@ -0,0 +1,19 @@
|
|
1
|
+
.direct-upload {
|
2
|
+
margin-bottom: 0.75em;
|
3
|
+
}
|
4
|
+
|
5
|
+
.direct-upload--pending {
|
6
|
+
opacity: 0.6;
|
7
|
+
}
|
8
|
+
|
9
|
+
.direct-upload--complete .direct-upload__progress {
|
10
|
+
opacity: 0.4;
|
11
|
+
}
|
12
|
+
|
13
|
+
.direct-upload--error {
|
14
|
+
border-color: red;
|
15
|
+
}
|
16
|
+
|
17
|
+
input[type=file][data-direct-upload-url][disabled] {
|
18
|
+
display: none;
|
19
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
.resumable-upload {
|
2
|
+
margin-bottom: 0.75em;
|
3
|
+
}
|
4
|
+
|
5
|
+
.resumable-upload--pending {
|
6
|
+
opacity: 0.6;
|
7
|
+
}
|
8
|
+
|
9
|
+
.resumable-upload--complete .resumable-upload__progress {
|
10
|
+
opacity: 0.4;
|
11
|
+
}
|
12
|
+
|
13
|
+
.resumable-upload--error {
|
14
|
+
border-color: red;
|
15
|
+
}
|
16
|
+
|
17
|
+
input[type=file][data-resumable-upload-url][disabled] {
|
18
|
+
display: none;
|
19
|
+
}
|
@@ -5,10 +5,15 @@ module Kadim
|
|
5
5
|
protect_from_forgery with: :exception
|
6
6
|
before_action :import_main_app_polymorphic_mappings
|
7
7
|
append_view_path Kadim::MemoryResolver.instance
|
8
|
+
layout :kadim_layout
|
8
9
|
|
9
10
|
private
|
10
11
|
def import_main_app_polymorphic_mappings
|
11
12
|
Kadim::Engine.routes.polymorphic_mappings.merge! Rails.application.routes.polymorphic_mappings
|
12
13
|
end
|
14
|
+
|
15
|
+
def kadim_layout
|
16
|
+
Kadim.layout ? "kadim/#{Kadim.layout}/application" : "kadim/application"
|
17
|
+
end
|
13
18
|
end
|
14
19
|
end
|
@@ -4,12 +4,16 @@ module Kadim
|
|
4
4
|
module ApplicationHelper
|
5
5
|
def menu_links
|
6
6
|
kadim_link = link_to("Kadim", kadim.root_path)
|
7
|
-
links =
|
8
|
-
link_to model_klass.model_name.human(count: :many), model_klass
|
9
|
-
end
|
7
|
+
links = raw_menu_links
|
10
8
|
safe_join([kadim_link] + links, " | ")
|
11
9
|
end
|
12
10
|
|
11
|
+
def raw_menu_links(options = {})
|
12
|
+
Kadim.app_model_paths.map(&:camelize).map(&:constantize).map do |model_klass|
|
13
|
+
active_link_to model_klass.model_name.human(count: :many), model_klass, **options
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
13
17
|
def upload_type
|
14
18
|
case Kadim.upload_type
|
15
19
|
when :direct then { direct_upload: true }
|
@@ -18,6 +22,14 @@ module Kadim
|
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
25
|
+
def invalid_attribute_class(model, attribute)
|
26
|
+
attribute_invalid?(model, attribute) ? "is-danger" : ""
|
27
|
+
end
|
28
|
+
|
29
|
+
def attribute_invalid?(model, attribute)
|
30
|
+
model.errors.key?(attribute)
|
31
|
+
end
|
32
|
+
|
21
33
|
def method_missing(method, *args, &block)
|
22
34
|
if method.to_s.end_with?("_path", "_url")
|
23
35
|
if main_app.respond_to?(method)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
7
|
+
<title>Kadim</title>
|
8
|
+
<%= csrf_meta_tags %>
|
9
|
+
<%= csp_meta_tag %>
|
10
|
+
|
11
|
+
<%= stylesheet_link_tag 'kadim/bulma/application', media: 'all', 'data-turbolinks-track': 'reload' %>
|
12
|
+
<%= javascript_include_tag 'kadim/bulma/application', 'data-turbolinks-track': 'reload', defer: true %>
|
13
|
+
<link href="https://fonts.googleapis.com/css?family=Muli&display=swap" rel="stylesheet">
|
14
|
+
</head>
|
15
|
+
<body>
|
16
|
+
<nav class="navbar is-hidden-desktop">
|
17
|
+
<div class="container">
|
18
|
+
<div class="navbar-brand">
|
19
|
+
<a class="navbar-item brand-text" href="../index.html">kadim</a>
|
20
|
+
<div class="navbar-burger burger" data-target="kadim-menu">
|
21
|
+
<span></span>
|
22
|
+
<span></span>
|
23
|
+
<span></span>
|
24
|
+
</div>
|
25
|
+
|
26
|
+
</div>
|
27
|
+
<div id="kadim-menu" class="navbar-menu is-hidden-desktop">
|
28
|
+
<div class="navbar-start">
|
29
|
+
<% raw_menu_links(class: "navbar-item").each do |link| %>
|
30
|
+
<%= link %>
|
31
|
+
<% end %>
|
32
|
+
</div>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
</nav>
|
36
|
+
|
37
|
+
<div class="columns is-marginless">
|
38
|
+
<div class="column is-2 is-flex menu-container">
|
39
|
+
<div class="columns brand-container">
|
40
|
+
<div class="column is-12 has-background-dark has-text-centered">
|
41
|
+
<%= link_to 'kadim', root_path, class: "has-text-danger is-size-4 is-block is-fullwidth brand-text" %>
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
<div class="columns models-container">
|
45
|
+
<div class="column is-12 has-background-dark is-hidden-mobile models-container--column">
|
46
|
+
<aside class="menu">
|
47
|
+
<p class="menu-label">
|
48
|
+
Models
|
49
|
+
</p>
|
50
|
+
<ul class="menu-list">
|
51
|
+
<% raw_menu_links(class_active: "is-active").each do |link| %>
|
52
|
+
<li><%= link %></li>
|
53
|
+
<% end %>
|
54
|
+
</ul>
|
55
|
+
</aside>
|
56
|
+
</div>
|
57
|
+
</div>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<div class="column is-10">
|
61
|
+
<div class="content">
|
62
|
+
<%= yield %>
|
63
|
+
</div>
|
64
|
+
</div>
|
65
|
+
</div>
|
66
|
+
</body>
|
67
|
+
</html>
|
@@ -18,6 +18,8 @@ module Kadim
|
|
18
18
|
end
|
19
19
|
|
20
20
|
@initial_args += Kadim.scaffold_attributes(model_path.camelize.constantize) if @initial_args.one?
|
21
|
+
@initial_options.reject! { |option| option.starts_with?("-e") || option.starts_with?("--template-engine") }
|
22
|
+
@initial_options << "--template-engine=erb"
|
21
23
|
|
22
24
|
Kadim.scaffold_controller(@initial_args, @initial_options, @initial_config)
|
23
25
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
<%%= form_with(model: <%= model_resource_name %>, local: true) do |form| %>
|
2
|
+
<%% if <%= singular_table_name %>.errors.any? %>
|
3
|
+
<article id="error_explanation" class="message is-danger">
|
4
|
+
<div class="message-body">
|
5
|
+
<p><%%= pluralize(<%= singular_table_name %>.errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:</p>
|
6
|
+
<ul>
|
7
|
+
<%% <%= singular_table_name %>.errors.full_messages.each do |message| %>
|
8
|
+
<li><%%= message %></li>
|
9
|
+
<%% end %>
|
10
|
+
</ul>
|
11
|
+
</div>
|
12
|
+
</article>
|
13
|
+
<%% end %>
|
14
|
+
|
15
|
+
<% attributes.each do |attribute| -%>
|
16
|
+
<div class="field is-horizontal">
|
17
|
+
<% if attribute.password_digest? -%>
|
18
|
+
<div class="field-label is-normal">
|
19
|
+
<%%= form.label :password, class: "label" %>
|
20
|
+
</div>
|
21
|
+
<div class="field-body">
|
22
|
+
<div class="field">
|
23
|
+
<div class="control">
|
24
|
+
<%%= form.password_field :password, class: "input" %>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<div class="field is-horizontal">
|
31
|
+
<div class="field-label is-normal">
|
32
|
+
<%%= form.label :password_confirmation, class: "label" %>
|
33
|
+
</div>
|
34
|
+
<div class="field-body">
|
35
|
+
<div class="field">
|
36
|
+
<div class="control">
|
37
|
+
<%%= form.password_field :password_confirmation, class: "input" %>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
<% elsif attribute.attachment? -%>
|
42
|
+
<div class="field-label is-normal">
|
43
|
+
<%%= form.label :<%= attribute.column_name %>, class: "label" %>
|
44
|
+
</div>
|
45
|
+
<div class="field-body">
|
46
|
+
<div class="field">
|
47
|
+
<div class="control">
|
48
|
+
<div class="file">
|
49
|
+
<label class="file-label">
|
50
|
+
<%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, **upload_type, class: "file-input" %>
|
51
|
+
<span class="file-cta">
|
52
|
+
<span class="file-icon">
|
53
|
+
<i class="fas fa-upload"></i>
|
54
|
+
</span>
|
55
|
+
<span class="file-label">Choose a file...</span>
|
56
|
+
</span>
|
57
|
+
</label>
|
58
|
+
</div>
|
59
|
+
</div>
|
60
|
+
</div>
|
61
|
+
</div>
|
62
|
+
<% elsif attribute.attachments? -%>
|
63
|
+
<div class="field-label is-normal">
|
64
|
+
<%%= form.label :<%= attribute.column_name %>, class: "label" %>
|
65
|
+
</div>
|
66
|
+
<div class="field-body">
|
67
|
+
<div class="field">
|
68
|
+
<div class="control">
|
69
|
+
<div class="file">
|
70
|
+
<label class="file-label">
|
71
|
+
<%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, **upload_type, multiple: true, class: "file-input" %>
|
72
|
+
<span class="file-cta">
|
73
|
+
<span class="file-icon">
|
74
|
+
<i class="fas fa-upload"></i>
|
75
|
+
</span>
|
76
|
+
<span class="file-label">Choose files...</span>
|
77
|
+
</span>
|
78
|
+
</label>
|
79
|
+
</div>
|
80
|
+
</div>
|
81
|
+
</div>
|
82
|
+
</div>
|
83
|
+
<% else -%>
|
84
|
+
<div class="field-label is-normal">
|
85
|
+
<%%= form.label :<%= attribute.column_name %>, class: "label" %>
|
86
|
+
</div>
|
87
|
+
<div class="field-body">
|
88
|
+
<div class="field">
|
89
|
+
<div class="control">
|
90
|
+
<%%= form.<%= attribute.field_type %> :<%= attribute.column_name %>, class: "input #{invalid_attribute_class(<%= singular_table_name %>, :<%= attribute.column_name %>)}" %>
|
91
|
+
<%% if attribute_invalid?(<%= singular_table_name %>, :<%= attribute.column_name %>) %>
|
92
|
+
<p class="help is-danger"><%%= <%= singular_table_name %>.errors[:<%= attribute.column_name %>].to_sentence %></p>
|
93
|
+
<%% end %>
|
94
|
+
</div>
|
95
|
+
</div>
|
96
|
+
</div>
|
97
|
+
<% end -%>
|
98
|
+
</div>
|
99
|
+
|
100
|
+
<% end -%>
|
101
|
+
<section class="box has-background-light is-shadowless">
|
102
|
+
<div class="columns">
|
103
|
+
<div class="column is-11 is-offset-1">
|
104
|
+
<%%= form.submit class: "button is-primary" %>
|
105
|
+
<%% if action_name == "edit" %>
|
106
|
+
<%%= link_to 'Show', @<%= singular_table_name %>, class: "button" %>
|
107
|
+
<%% end %>
|
108
|
+
<%%= link_to 'Back', <%= index_helper %>_path, class: "button" %>
|
109
|
+
</div>
|
110
|
+
</div>
|
111
|
+
</section>
|
112
|
+
<%% end %>
|
@@ -0,0 +1,49 @@
|
|
1
|
+
<%%= render 'notice' %>
|
2
|
+
|
3
|
+
<h1><%= plural_table_name.titleize %></h1>
|
4
|
+
|
5
|
+
<%% if @<%= plural_table_name %>.any? %>
|
6
|
+
<div class="table-container">
|
7
|
+
<table class="table is-striped">
|
8
|
+
<thead>
|
9
|
+
<tr>
|
10
|
+
<th>#</th>
|
11
|
+
<% attributes.reject(&:password_digest?).each do |attribute| -%>
|
12
|
+
<th><%= attribute.human_name %></th>
|
13
|
+
<% end -%>
|
14
|
+
<th colspan="3"></th>
|
15
|
+
</tr>
|
16
|
+
</thead>
|
17
|
+
|
18
|
+
<tbody>
|
19
|
+
<%% @<%= plural_table_name %>.each do |<%= singular_table_name %>| %>
|
20
|
+
<tr>
|
21
|
+
<td><%%= <%= singular_table_name %>.id %></td>
|
22
|
+
<% attributes.reject(&:password_digest?).each do |attribute| -%>
|
23
|
+
<td><%%= <%= singular_table_name %>.<%= attribute.column_name %> %></td>
|
24
|
+
<% end -%>
|
25
|
+
<td class="action">
|
26
|
+
<%%= link_to icon("far", "eye"), <%= model_resource_name %>, title: "Show" %>
|
27
|
+
<%%= link_to icon("far", "edit"), edit_<%= singular_route_name %>_path(<%= singular_table_name %>), title: "Edit" %>
|
28
|
+
<%%= link_to icon("far", "trash-alt"), <%= model_resource_name %>, title: "Destroy", method: :delete, data: { confirm: 'Are you sure?' } %>
|
29
|
+
</td>
|
30
|
+
</tr>
|
31
|
+
<%% end %>
|
32
|
+
</tbody>
|
33
|
+
</table>
|
34
|
+
</div>
|
35
|
+
<%% else %>
|
36
|
+
<div class="box is-shadowless has-background-light has-text-centered">
|
37
|
+
<%%= icon("fas", "cloud-sun", class: "fa-5x", style: "padding-bottom: 15px") %>
|
38
|
+
<br />
|
39
|
+
Let's input some data?
|
40
|
+
</div>
|
41
|
+
<%% end %>
|
42
|
+
|
43
|
+
<section class="box has-background-light is-shadowless">
|
44
|
+
<div class="columns">
|
45
|
+
<div class="column is-11 is-offset-1">
|
46
|
+
<%%= link_to 'New <%= singular_table_name.titleize %>', new_<%= singular_route_name %>_path, class: "button is-link" %>
|
47
|
+
</div>
|
48
|
+
</div>
|
49
|
+
</section>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
<%%= render 'notice' %>
|
2
|
+
|
3
|
+
<h1><%= singular_table_name.titleize %> #<%%= @<%= singular_table_name %>.id %> - <%%= @<%= singular_table_name %>.to_s %></h1>
|
4
|
+
|
5
|
+
<% attributes.reject(&:password_digest?).each do |attribute| -%>
|
6
|
+
<div class="columns">
|
7
|
+
<div class="column is-3 has-text-right">
|
8
|
+
<strong><%= attribute.human_name %>:</strong>
|
9
|
+
</div>
|
10
|
+
<div class="column">
|
11
|
+
<% if attribute.attachment? -%>
|
12
|
+
<%%= link_to @<%= singular_table_name %>.<%= attribute.column_name %>.filename, @<%= singular_table_name %>.<%= attribute.column_name %> if @<%= singular_table_name %>.<%= attribute.column_name %>.attached? %>
|
13
|
+
<% elsif attribute.attachments? -%>
|
14
|
+
<%% @<%= singular_table_name %>.<%= attribute.column_name %>.each do |<%= attribute.singular_name %>| %>
|
15
|
+
<div><%%= link_to <%= attribute.singular_name %>.filename, <%= attribute.singular_name %> %></div>
|
16
|
+
<%% end %>
|
17
|
+
<% else -%>
|
18
|
+
<%%= @<%= singular_table_name %>.<%= attribute.column_name %> %>
|
19
|
+
<% end -%>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<% end -%>
|
24
|
+
|
25
|
+
<section class="box has-background-light is-shadowless">
|
26
|
+
<div class="columns">
|
27
|
+
<div class="column is-11 is-offset-1">
|
28
|
+
<%%= link_to 'Edit', edit_<%= singular_table_name %>_path(@<%= singular_table_name %>), class: "button is-primary" %>
|
29
|
+
<%%= link_to 'Back', <%= index_helper %>_path, class: "button" %>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</section>
|
data/lib/kadim.rb
CHANGED
@@ -8,13 +8,21 @@ module Kadim
|
|
8
8
|
# @return [Symbol] current upload type used to scaffold file input fields
|
9
9
|
# @overload upload_type=(value)
|
10
10
|
# You can use the following symbols to set the upload type:
|
11
|
-
# *
|
12
|
-
# *
|
13
|
-
# *
|
11
|
+
# * :local - Uses ActiveStorage {https://guides.rubyonrails.org/active_storage_overview.html#disk-service Disk Service}
|
12
|
+
# * :direct - Uses ActiveStorage {https://guides.rubyonrails.org/active_storage_overview.html#direct-uploads Direct Upload}
|
13
|
+
# * :resumable - Uses {https://rubygems.org/gems/activestorage-resumable activestorage-resumable gem} to implement {https://cloud.google.com/storage/docs/performing-resumable-uploads Resumable Uploads} (supports only GCS)
|
14
14
|
# @param value [Symbol]
|
15
15
|
# @return [Symbol]
|
16
16
|
mattr_accessor :upload_type
|
17
17
|
|
18
|
+
# @overload layout
|
19
|
+
# @return [Symbol, nil] current layout or nil for the default Rails layout for scaffold_controller
|
20
|
+
# @overload layout=(value)
|
21
|
+
# The following layouts are available:
|
22
|
+
# * nil - No layout, will use Rails generators template.
|
23
|
+
# * :bulma - A layout using the {https://bulma.io bulma} CSS framework.
|
24
|
+
mattr_accessor :layout
|
25
|
+
|
18
26
|
def self.init
|
19
27
|
@@upload_type ||= if [:amazon, :google, :microsoft].include?(Rails.configuration.active_storage.service)
|
20
28
|
:direct
|
@@ -50,9 +58,12 @@ module Kadim
|
|
50
58
|
Rails::Generators.namespace = Kadim
|
51
59
|
|
52
60
|
require "rails/generators/erb/scaffold/scaffold_generator"
|
53
|
-
|
54
|
-
|
55
|
-
|
61
|
+
templates_path = if Kadim.layout == :bulma
|
62
|
+
"generators/kadim/scaffold_controller/templates/bulma"
|
63
|
+
else
|
64
|
+
"generators/kadim/scaffold_controller/templates"
|
65
|
+
end
|
66
|
+
Erb::Generators::ScaffoldGenerator.source_paths.prepend(File.expand_path(templates_path, __dir__))
|
56
67
|
|
57
68
|
generator = Rails::Generators::ScaffoldControllerGenerator.new(args, options, config)
|
58
69
|
source_path_idx = generator.class.source_paths.index { |source_path| source_path.include?("jbuilder") }
|
data/lib/kadim/engine.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_link_to"
|
3
4
|
require "activestorage/resumable"
|
5
|
+
require "bulma-rails"
|
6
|
+
require "font-awesome-sass"
|
4
7
|
|
5
8
|
module Kadim
|
6
9
|
class Engine < ::Rails::Engine
|
@@ -10,6 +13,8 @@ module Kadim
|
|
10
13
|
config.assets.precompile += %w[
|
11
14
|
kadim/application.css
|
12
15
|
kadim/application.js
|
16
|
+
kadim/bulma/application.css
|
17
|
+
kadim/bulma/application.js
|
13
18
|
]
|
14
19
|
end
|
15
20
|
|
data/lib/kadim/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kadim
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kadu Diógenes
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-01-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: active_link_to
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.0
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: activestorage-resumable
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -24,6 +38,34 @@ dependencies:
|
|
24
38
|
- - "~>"
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: 1.0.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bulma-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.8.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.8.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: font-awesome-sass
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 5.9.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 5.9.0
|
27
69
|
- !ruby/object:Gem::Dependency
|
28
70
|
name: google-cloud-storage
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -204,14 +246,28 @@ dependencies:
|
|
204
246
|
requirements:
|
205
247
|
- - "~>"
|
206
248
|
- !ruby/object:Gem::Version
|
207
|
-
version:
|
249
|
+
version: 4.0.0.beta3
|
250
|
+
type: :development
|
251
|
+
prerelease: false
|
252
|
+
version_requirements: !ruby/object:Gem::Requirement
|
253
|
+
requirements:
|
254
|
+
- - "~>"
|
255
|
+
- !ruby/object:Gem::Version
|
256
|
+
version: 4.0.0.beta3
|
257
|
+
- !ruby/object:Gem::Dependency
|
258
|
+
name: rails-controller-testing
|
259
|
+
requirement: !ruby/object:Gem::Requirement
|
260
|
+
requirements:
|
261
|
+
- - "~>"
|
262
|
+
- !ruby/object:Gem::Version
|
263
|
+
version: 1.0.4
|
208
264
|
type: :development
|
209
265
|
prerelease: false
|
210
266
|
version_requirements: !ruby/object:Gem::Requirement
|
211
267
|
requirements:
|
212
268
|
- - "~>"
|
213
269
|
- !ruby/object:Gem::Version
|
214
|
-
version:
|
270
|
+
version: 1.0.4
|
215
271
|
- !ruby/object:Gem::Dependency
|
216
272
|
name: rubocop-performance
|
217
273
|
requirement: !ruby/object:Gem::Requirement
|
@@ -282,6 +338,20 @@ dependencies:
|
|
282
338
|
- - "~>"
|
283
339
|
- !ruby/object:Gem::Version
|
284
340
|
version: 3.142.0
|
341
|
+
- !ruby/object:Gem::Dependency
|
342
|
+
name: shoulda-matchers
|
343
|
+
requirement: !ruby/object:Gem::Requirement
|
344
|
+
requirements:
|
345
|
+
- - "~>"
|
346
|
+
- !ruby/object:Gem::Version
|
347
|
+
version: 4.1.2
|
348
|
+
type: :development
|
349
|
+
prerelease: false
|
350
|
+
version_requirements: !ruby/object:Gem::Requirement
|
351
|
+
requirements:
|
352
|
+
- - "~>"
|
353
|
+
- !ruby/object:Gem::Version
|
354
|
+
version: 4.1.2
|
285
355
|
- !ruby/object:Gem::Dependency
|
286
356
|
name: sqlite3
|
287
357
|
requirement: !ruby/object:Gem::Requirement
|
@@ -322,23 +392,36 @@ files:
|
|
322
392
|
- Rakefile
|
323
393
|
- app/assets/config/kadim_manifest.js
|
324
394
|
- app/assets/javascripts/kadim/application.js
|
395
|
+
- app/assets/javascripts/kadim/bulma/application.js
|
396
|
+
- app/assets/javascripts/kadim/bulma/direct_upload.js
|
397
|
+
- app/assets/javascripts/kadim/bulma/resumable_upload.js
|
325
398
|
- app/assets/javascripts/kadim/direct_upload.js
|
326
399
|
- app/assets/javascripts/kadim/resumable_upload.js
|
327
400
|
- app/assets/stylesheets/kadim/application.css
|
401
|
+
- app/assets/stylesheets/kadim/bulma/application.scss
|
402
|
+
- app/assets/stylesheets/kadim/bulma/direct_upload.css
|
403
|
+
- app/assets/stylesheets/kadim/bulma/resumable_upload.css
|
328
404
|
- app/assets/stylesheets/kadim/direct_upload.css
|
329
405
|
- app/assets/stylesheets/kadim/resumable_upload.css
|
330
406
|
- app/controllers/kadim/application_controller.rb
|
331
407
|
- app/helpers/kadim/application_helper.rb
|
332
408
|
- app/jobs/kadim/application_job.rb
|
333
409
|
- app/mailers/kadim/application_mailer.rb
|
410
|
+
- app/views/kadim/application/_notice.html.erb
|
334
411
|
- app/views/kadim/application/index.html.erb
|
335
412
|
- app/views/layouts/kadim/application.html.erb
|
413
|
+
- app/views/layouts/kadim/bulma/application.html.erb
|
336
414
|
- config/routes.rb
|
337
415
|
- lib/generators/kadim/host/USAGE
|
338
416
|
- lib/generators/kadim/host/host_generator.rb
|
339
417
|
- lib/generators/kadim/scaffold_controller/USAGE
|
340
418
|
- lib/generators/kadim/scaffold_controller/scaffold_controller_generator.rb
|
341
419
|
- lib/generators/kadim/scaffold_controller/templates/_form.html.erb.tt
|
420
|
+
- lib/generators/kadim/scaffold_controller/templates/bulma/_form.html.erb.tt
|
421
|
+
- lib/generators/kadim/scaffold_controller/templates/bulma/edit.html.erb.tt
|
422
|
+
- lib/generators/kadim/scaffold_controller/templates/bulma/index.html.erb.tt
|
423
|
+
- lib/generators/kadim/scaffold_controller/templates/bulma/new.html.erb.tt
|
424
|
+
- lib/generators/kadim/scaffold_controller/templates/bulma/show.html.erb.tt
|
342
425
|
- lib/kadim.rb
|
343
426
|
- lib/kadim/engine.rb
|
344
427
|
- lib/kadim/template/memory_resolver.rb
|