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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cfb2766e697bcfdfec4e606aca5398f9f4ff673d3cd06daf91bc415609d3fe94
4
- data.tar.gz: 1e2a9a1cc54e158157492e54db31b7391d31ab8cf5bbb96fc910198c3337a118
3
+ metadata.gz: 969b2f040793f3267751ee1fcb3a66ca7fa7378858a21259a1ac882b68fec8c8
4
+ data.tar.gz: 05e6d286c7780728b386de66614001b4f530be45a3b83e808637262d43091f63
5
5
  SHA512:
6
- metadata.gz: 742c0dc9e953a303c4105e16bb5e9a32c41d19949046dd309f913071e9fdbfd806ac5f21df4793e84cc1273a8bda348eb3b56dda8e0fb34184f5277dfd406a7d
7
- data.tar.gz: f9d5722c64dc28cac554d1bce33ad942e6fc9140e9b0189191556852fc6d20d2868f62f133899bf674b436a773136c012015aa83c7abc11a136b4c5a64ad2df2
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
- - [ ] Add a beautiful look and feel
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).
@@ -1,4 +1,4 @@
1
1
  //= require rails-ujs
2
2
  //= require activestorage
3
3
  //= require activestorage-resumable
4
- //= require_tree .
4
+ //= require_directory .
@@ -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
+ })
@@ -10,6 +10,6 @@
10
10
  * files in this directory. Styles in this file should be added after the last require_* statement.
11
11
  * It is generally better to create a new file per style scope.
12
12
  *
13
- *= require_tree .
13
+ *= require_directory .
14
14
  *= require_self
15
15
  */
@@ -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 = Kadim.app_model_paths.map(&:camelize).map(&:constantize).map do |model_klass|
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,6 @@
1
+ <% if notice %>
2
+ <div class="notification is-info">
3
+ <button class="delete"></button>
4
+ <%= notice %>
5
+ </div>
6
+ <% end %>
@@ -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,5 @@
1
+ <%%= render 'notice' %>
2
+
3
+ <h1>Editing <%= singular_table_name.titleize %></h1>
4
+
5
+ <%%= render 'form', <%= singular_table_name %>: @<%= singular_table_name %> %>
@@ -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,5 @@
1
+ <%%= render 'notice' %>
2
+
3
+ <h1>New <%= singular_table_name.titleize %></h1>
4
+
5
+ <%%= render 'form', <%= singular_table_name %>: @<%= singular_table_name %> %>
@@ -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>
@@ -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
- # * +: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)
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
- Erb::Generators::ScaffoldGenerator.source_paths.prepend(
54
- File.expand_path("generators/kadim/scaffold_controller/templates", __dir__)
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") }
@@ -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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kadim
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
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.3.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: 2019-12-19 00:00:00.000000000 Z
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: 3.8.0
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: 3.8.0
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