kadim 0.3.0 → 0.4.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 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