mega_scaffold 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +146 -0
- data/Rakefile +3 -0
- data/app/views/mega_scaffold/_css.html.erb +48 -0
- data/app/views/mega_scaffold/_form.html.erb +64 -0
- data/app/views/mega_scaffold/edit.html.erb +7 -0
- data/app/views/mega_scaffold/index.html.erb +55 -0
- data/app/views/mega_scaffold/new.html.erb +7 -0
- data/app/views/mega_scaffold/show.html.erb +18 -0
- data/lib/mega_scaffold/code_generator.rb +77 -0
- data/lib/mega_scaffold/controller.rb +88 -0
- data/lib/mega_scaffold/engine.rb +13 -0
- data/lib/mega_scaffold/fields_generator.rb +18 -0
- data/lib/mega_scaffold/helpers.rb +23 -0
- data/lib/mega_scaffold/klass_decorator.rb +69 -0
- data/lib/mega_scaffold/routing.rb +47 -0
- data/lib/mega_scaffold/type_detector.rb +33 -0
- data/lib/mega_scaffold/version.rb +3 -0
- data/lib/mega_scaffold.rb +12 -0
- metadata +249 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 702f2689fb4ca9497026509a03ebf15fa89c1ced0e74835b1e9660e4d55780b9
|
4
|
+
data.tar.gz: d818c54ba05bb8d91090a0680b626a285a07a9e6cdcfc7e4047862ee53f3634b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4e15fa19562298c8dcad4cf7f70136e8aaeaad16baf5e15a186a4350de32a061174d854a8c675eb0f01d75c62798c2c9061f5638c69aed4dfe180359ff2f4b20
|
7
|
+
data.tar.gz: 710922460e48cc82c8873d259b336866ffa20986955c921d324246ad14b4381f84cec8b6a4077886361aae02b86340afb77458d7d1f9f0f5056c09bcd3bee0a4
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2022
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
# mega_scaffold
|
2
|
+
|
3
|
+
This is the FASTEST way how to add CRUD functionality for your models. Literally by just adding ONE LINE of code in `routes.rb`.
|
4
|
+
|
5
|
+
With additional customization options it allows you to build quickly admin panels or simple controllers to output data.
|
6
|
+
|
7
|
+
It works with existing models so all your validations, associations, etc will work as usual.
|
8
|
+
|
9
|
+
![rails scaffold generator](docs/mega_scaffold.png)
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
1) add gem to Gemfile `gem "mega_scaffold"`
|
14
|
+
|
15
|
+
2) open `routes.rb`
|
16
|
+
|
17
|
+
3) add `mega_scaffold :categories` (if you have `Category` model)
|
18
|
+
|
19
|
+
|
20
|
+
## Customization
|
21
|
+
|
22
|
+
- configure layout
|
23
|
+
- specify how to fetch records
|
24
|
+
- specify type for input
|
25
|
+
- configure to work with associations
|
26
|
+
- access helpers to output value
|
27
|
+
- hide columns
|
28
|
+
- change labels
|
29
|
+
- specify which fields where to show
|
30
|
+
- provide additional options for form fields
|
31
|
+
|
32
|
+
If you need examples of customization (see `test/dummy` as an example):
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
# routes.rb
|
36
|
+
|
37
|
+
Rails.application.routes.draw do
|
38
|
+
|
39
|
+
# could be nested in existing resources
|
40
|
+
resources :companies do
|
41
|
+
mega_scaffold :attachments,
|
42
|
+
parent: -> (controller) { Company.find(controller.params[:company_id]) },
|
43
|
+
collection: -> (controller) { controller.parent.attachments },
|
44
|
+
fields: [
|
45
|
+
{ name: :id, view: :index },
|
46
|
+
{ name: :file, type: :file_field, view: :all, value: -> (record, view) { view.link_to 'Download', record.file.url } },
|
47
|
+
{ name: :created_at, view: :index },
|
48
|
+
]
|
49
|
+
end
|
50
|
+
|
51
|
+
# could be added to namespaces
|
52
|
+
namespace :secret do
|
53
|
+
namespace :admin do
|
54
|
+
mega_scaffold :categories, fields: [
|
55
|
+
{ name: :id, view: :index },
|
56
|
+
{ name: :name, view: :all, value: -> (record, _) { record.name&.upcase } },
|
57
|
+
{ name: :accounts, view: [:index, :show], value: -> (record, _) { record.accounts.count } },
|
58
|
+
{ name: :created_at, view: [:index, :show], value: -> (record, _) { I18n.l record.created_at, format: :short } },
|
59
|
+
]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# simple usage, you can specify concerns with "before_action" or other contoller-related logic
|
64
|
+
mega_scaffold :users,
|
65
|
+
collection: -> (_) { User.ordered },
|
66
|
+
concerns: [Protected],
|
67
|
+
layout: 'admin',
|
68
|
+
only: [:id, :name, :age, :dob, :country, :created_at, :phone]
|
69
|
+
|
70
|
+
# usage with file upload and showing images in the index and show views
|
71
|
+
mega_scaffold :photos,
|
72
|
+
fields: [
|
73
|
+
{ name: :user, column: :user_id, view: :all, type: :select, collection: -> { User.by_name.map{|e| [e.name, e.id]} }, value: -> (record, view) { view.link_to_if record.user, record.user&.name, record.user } },
|
74
|
+
{ name: :photo, type: :file_field, view: :all, value: -> (record, view) { view.image_tag record.photo.url, style: 'width: 200px' } },
|
75
|
+
{ name: :created_at, view: :index, value: -> (record, view) { view.l record.created_at, format: :long } },
|
76
|
+
]
|
77
|
+
|
78
|
+
# access to different set of records (for example admin can see all records and all other users only own) + form with associations
|
79
|
+
mega_scaffold :accounts,
|
80
|
+
collection: -> (controller) { controller.admin? ? Account.all : current_user.accounts },
|
81
|
+
fields: [
|
82
|
+
{ name: :id, view: [:show] },
|
83
|
+
{ name: :name, type: :text_field, view: :all, value: -> (record, view) { view.link_to record.name.to_s.upcase, record } },
|
84
|
+
{
|
85
|
+
view: :all,
|
86
|
+
name: :account_type,
|
87
|
+
type: :collection_select,
|
88
|
+
collection: -> { Account::TYPES },
|
89
|
+
options: [:to_s, :to_s, include_blank: true]
|
90
|
+
},
|
91
|
+
{
|
92
|
+
view: :all,
|
93
|
+
name: :priority,
|
94
|
+
type: :range_field,
|
95
|
+
options: { min: 0, max: 100, step: 10 }
|
96
|
+
},
|
97
|
+
{
|
98
|
+
name: :categories,
|
99
|
+
column: {
|
100
|
+
name: :category_ids,
|
101
|
+
permit: [],
|
102
|
+
},
|
103
|
+
type: :collection_check_boxes,
|
104
|
+
options: [:id, :name],
|
105
|
+
view: :form,
|
106
|
+
collection: -> { Category.by_name },
|
107
|
+
value: {
|
108
|
+
index: -> (record, _) { record.categories.count },
|
109
|
+
show: -> (record, _) { record.categories.pluck(:name).join(", ") }
|
110
|
+
}
|
111
|
+
},
|
112
|
+
{ name: 'VIRTUAL ATTR', type: :virtual, view: :index, value: -> (record, view) { "ID: #{record.id}" } },
|
113
|
+
{
|
114
|
+
name: :owner_id,
|
115
|
+
label: "Owner",
|
116
|
+
view: :all,
|
117
|
+
options: { include_blank: true },
|
118
|
+
type: :select,
|
119
|
+
collection: -> { User.by_name.map{|e| [e.name, e.id]} },
|
120
|
+
value: -> (record, _) { record.owner&.name }
|
121
|
+
},
|
122
|
+
{ name: :created_at, view: [:index, :show], value: -> (record, _) { I18n.l(record.created_at, format: :long) } },
|
123
|
+
]
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
127
|
+
## TODO Ideas
|
128
|
+
|
129
|
+
- config for actions
|
130
|
+
- more specs
|
131
|
+
- simple search using ransack?
|
132
|
+
- how to overide view instructions
|
133
|
+
- check if all is ok with turbo/turbolinks
|
134
|
+
- export to CSV, JSON?
|
135
|
+
- integration with pundit or cancancan?
|
136
|
+
- support for "resource" type
|
137
|
+
- work with I18n to translate labels?
|
138
|
+
- how to customize views
|
139
|
+
- view customization per controller
|
140
|
+
|
141
|
+
## Contributing
|
142
|
+
|
143
|
+
You are welcome to contribute.
|
144
|
+
|
145
|
+
## License
|
146
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
<style>
|
2
|
+
.mega_scaffold_container {
|
3
|
+
padding-top: 20px;
|
4
|
+
}
|
5
|
+
|
6
|
+
.mega_scaffold_container .mega_scaffold_container_header {
|
7
|
+
display: flex;
|
8
|
+
justify-content: space-between;
|
9
|
+
align-items: center;
|
10
|
+
}
|
11
|
+
|
12
|
+
.mega_scaffold_container .field_with_errors input,
|
13
|
+
.mega_scaffold_container .field_with_errors select,
|
14
|
+
.mega_scaffold_container .field_with_errors textarea {
|
15
|
+
border: 1px solid red;
|
16
|
+
}
|
17
|
+
|
18
|
+
.mega_scaffold_container abbr {
|
19
|
+
color: red;
|
20
|
+
text-decoration: none;
|
21
|
+
}
|
22
|
+
|
23
|
+
.mega_scaffold_container table {
|
24
|
+
width: 100%;
|
25
|
+
}
|
26
|
+
|
27
|
+
.mega_scaffold_container form .mega_scaffold_field_multiple label {
|
28
|
+
padding-left: 5px;
|
29
|
+
margin-right: 10px;
|
30
|
+
}
|
31
|
+
|
32
|
+
.mega_scaffold_container .pagination > * {
|
33
|
+
margin-right: 10px;
|
34
|
+
}
|
35
|
+
|
36
|
+
.mega_scaffold_container .mega_scaffold_errors {
|
37
|
+
font-size: 14px;
|
38
|
+
color: red;
|
39
|
+
}
|
40
|
+
|
41
|
+
.mega_scaffold_container .mega_scaffold_errors h4 {
|
42
|
+
font-size: 16px;
|
43
|
+
}
|
44
|
+
|
45
|
+
.mega_scaffold_container input[type=submit] {
|
46
|
+
cursor: pointer;
|
47
|
+
}
|
48
|
+
</style>
|
@@ -0,0 +1,64 @@
|
|
1
|
+
<%= form_for record, url: url_for(action: record.persisted? ? :update : :create, mega_scaffold.pk => record.send(mega_scaffold.pk)) , data: { turbo: false, turbolinks: false } do |f| %>
|
2
|
+
<% if record.errors.any? %>
|
3
|
+
<div class="mega_scaffold_errors">
|
4
|
+
<h4><%= pluralize(record.errors.count, "error") %> prohibited this record from being saved:</h4>
|
5
|
+
<ul>
|
6
|
+
<% record.errors.each do |error| %>
|
7
|
+
<li><%= error.full_message %></li>
|
8
|
+
<% end %>
|
9
|
+
</ul>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<% mega_scaffold.form.each do |e| %>
|
14
|
+
<% type = e[:type].presence || :text_field %>
|
15
|
+
<% field = e[:column].presence || e[:name] %>
|
16
|
+
<% field = field.is_a?(Hash) ? field[:name] : field %>
|
17
|
+
<% label = mega_scaffold_field_name(e) %>
|
18
|
+
<% options = e[:options].presence || {} rescue {} %>
|
19
|
+
|
20
|
+
<%# default %>
|
21
|
+
<% options.merge!({ rows: 4, cols: 60 }) if type == :text_area %>
|
22
|
+
|
23
|
+
<div class="mega_scaffold_field">
|
24
|
+
<% if type.to_s =~ /collection/ %>
|
25
|
+
<label><%= label %></label>
|
26
|
+
<div class="mega_scaffold_field_multiple">
|
27
|
+
<%= f.send type, field, e[:collection].call, *options %>
|
28
|
+
</div>
|
29
|
+
|
30
|
+
<% elsif type == :select %>
|
31
|
+
<label>
|
32
|
+
<%= label %>
|
33
|
+
<div>
|
34
|
+
<%= f.select field, e[:collection].call, options %>
|
35
|
+
</div>
|
36
|
+
</label>
|
37
|
+
|
38
|
+
|
39
|
+
<% elsif type == :check_box %>
|
40
|
+
<label>
|
41
|
+
<%= f.check_box field, options %>
|
42
|
+
<%= label %>
|
43
|
+
</label>
|
44
|
+
|
45
|
+
|
46
|
+
<% else %>
|
47
|
+
<label>
|
48
|
+
<%= label %>
|
49
|
+
<div>
|
50
|
+
<%= f.send type, field, options %>
|
51
|
+
</div>
|
52
|
+
</label>
|
53
|
+
<% end %>
|
54
|
+
</div>
|
55
|
+
|
56
|
+
<% end %>
|
57
|
+
|
58
|
+
<br>
|
59
|
+
<br>
|
60
|
+
|
61
|
+
<%= f.submit %>
|
62
|
+
—
|
63
|
+
<%= link_to 'Back', { action: :index } %>
|
64
|
+
<% end %>
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<%= render 'mega_scaffold/css' %>
|
2
|
+
|
3
|
+
<div class="mega_scaffold_container">
|
4
|
+
<div class="mega_scaffold_container_header">
|
5
|
+
<h1><%= mega_scaffold.model.to_s.pluralize %></h1>
|
6
|
+
<div class="mega_scaffold_container_header_actions">
|
7
|
+
<%= link_to 'New', { action: :new }, { class: 'button btn btn-primary' } %>
|
8
|
+
<% if @parent %>
|
9
|
+
—
|
10
|
+
<%= link_to 'Back', mega_scaffold_parent_url %>
|
11
|
+
<% end %>
|
12
|
+
</div>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<table class="table">
|
16
|
+
<thead>
|
17
|
+
<tr>
|
18
|
+
<% mega_scaffold.columns.each do |field| %>
|
19
|
+
<th><%= mega_scaffold_field_name field %></th>
|
20
|
+
<% end %>
|
21
|
+
<th colspan="3"></th>
|
22
|
+
</tr>
|
23
|
+
</thead>
|
24
|
+
|
25
|
+
<tbody>
|
26
|
+
<% if @records.present? %>
|
27
|
+
<% @records.each do |record| %>
|
28
|
+
<tr>
|
29
|
+
<% mega_scaffold.columns.each do |field| %>
|
30
|
+
<td>
|
31
|
+
<%= mega_scaffold_value record, field, :index %>
|
32
|
+
</td>
|
33
|
+
<% end %>
|
34
|
+
<td><%= link_to 'show', { action: :show, mega_scaffold.pk => record.send(mega_scaffold.pk) } %></td>
|
35
|
+
<td><%= link_to 'edit', { action: :edit, mega_scaffold.pk => record.send(mega_scaffold.pk) } %></td>
|
36
|
+
<td><%= link_to 'delete', { action: :show, mega_scaffold.pk => record.send(mega_scaffold.pk) }, data: { method: :delete, turbo: false, turbolinks: false, confirm: "Are you sure?" } %></td>
|
37
|
+
</tr>
|
38
|
+
<% end %>
|
39
|
+
<% else %>
|
40
|
+
<tr>
|
41
|
+
<td colspan="<%= mega_scaffold.fields.size + 1 %>">
|
42
|
+
No Records.
|
43
|
+
</td>
|
44
|
+
</tr>
|
45
|
+
<% end %>
|
46
|
+
</tbody>
|
47
|
+
|
48
|
+
</table>
|
49
|
+
|
50
|
+
<% if respond_to?(:paginate) %>
|
51
|
+
<%= paginate @records %>
|
52
|
+
<% elsif respond_to?(:will_paginate) %>
|
53
|
+
<%= will_paginate @records %>
|
54
|
+
<% end %>
|
55
|
+
</div>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%= render 'mega_scaffold/css' %>
|
2
|
+
|
3
|
+
<div class="mega_scaffold_container">
|
4
|
+
<h1><%= mega_scaffold.model %></h1>
|
5
|
+
|
6
|
+
<dl>
|
7
|
+
<% mega_scaffold.show.each do |field| %>
|
8
|
+
<dt><%= mega_scaffold_field_name field %>:</dt>
|
9
|
+
<dd>
|
10
|
+
<%= mega_scaffold_value @record, field %>
|
11
|
+
</dd>
|
12
|
+
<% end %>
|
13
|
+
</dl>
|
14
|
+
|
15
|
+
<%= link_to 'Edit', { action: :edit, mega_scaffold.pk => @record.send(mega_scaffold.pk) }, { class: 'button' } %>
|
16
|
+
-
|
17
|
+
<%= link_to 'Back', { action: :index }, { class: 'button' } %>
|
18
|
+
</div>
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module MegaScaffold
|
2
|
+
class CodeGenerator
|
3
|
+
attr_reader :options
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate
|
10
|
+
strs = []
|
11
|
+
options[:namespaces].each_with_index do |e, index|
|
12
|
+
strs << "#{' ' * index}module #{index == 0 ? '::' + e : e}"
|
13
|
+
end
|
14
|
+
|
15
|
+
strs << %Q{
|
16
|
+
class #{'::' if options[:namespaces].empty?}#{options[:model].to_s.pluralize}Controller < ::ApplicationController
|
17
|
+
include MegaScaffold::Controller
|
18
|
+
include Helpers
|
19
|
+
|
20
|
+
#{"include " + options[:concerns].join(", ") if options[:concerns].any?}
|
21
|
+
|
22
|
+
helper :all
|
23
|
+
|
24
|
+
def parent
|
25
|
+
mega_scaffold.parent.call(self) if mega_scaffold.parent.is_a?(Proc)
|
26
|
+
end
|
27
|
+
|
28
|
+
def collection
|
29
|
+
mega_scaffold.collection.call(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
def resource
|
33
|
+
collection.find(params[:id])
|
34
|
+
end
|
35
|
+
|
36
|
+
def find_parent
|
37
|
+
@parent = parent
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def mega_scaffold
|
42
|
+
@mega_scaffold ||= OpenStruct.new({
|
43
|
+
scope: #{options[:scope][:as].to_s.to_json},
|
44
|
+
model: #{options[:model]},
|
45
|
+
pk: :#{options[:model].primary_key},
|
46
|
+
fields: self.class.fields_config,
|
47
|
+
columns: self.class.columns_config,
|
48
|
+
form: self.class.form_config,
|
49
|
+
show: self.class.show_config,
|
50
|
+
collection: self.class.collection_config,
|
51
|
+
parent: self.class.parent_config,
|
52
|
+
})
|
53
|
+
end
|
54
|
+
|
55
|
+
def detect_layout
|
56
|
+
"#{options[:layout].presence || 'application'}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
}
|
60
|
+
|
61
|
+
options[:namespaces].each_with_index do |e, index|
|
62
|
+
strs << "#{' ' * (options[:namespaces].size - index - 1)}end"
|
63
|
+
end
|
64
|
+
|
65
|
+
klass_name = "#{options[:namespaces].any? ? options[:namespaces].join("::") + "::" : '::'}#{options[:model].to_s.pluralize}Controller"
|
66
|
+
strs << klass_name
|
67
|
+
|
68
|
+
# delete old constant
|
69
|
+
# because it has conficts with same which was previosly initialized
|
70
|
+
# happend after changing routes.rb in dev mode
|
71
|
+
object_space = options[:namespaces].join("::").constantize rescue Object
|
72
|
+
object_space.send(:remove_const, :"#{options[:model].to_s.pluralize}Controller") rescue '='
|
73
|
+
|
74
|
+
strs.join("\n")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module MegaScaffold
|
2
|
+
module Controller
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
prepend_view_path("#{MegaScaffold::Engine.root}/app/views")
|
7
|
+
before_action :find_parent
|
8
|
+
layout :detect_layout
|
9
|
+
helper_method :mega_scaffold
|
10
|
+
end
|
11
|
+
|
12
|
+
def index
|
13
|
+
@records = if defined?(Kaminari)
|
14
|
+
collection.page(params[:page])
|
15
|
+
elsif defined?(WillPaginate)
|
16
|
+
collection.paginate(params[:page])
|
17
|
+
else
|
18
|
+
collection
|
19
|
+
end
|
20
|
+
render template: 'mega_scaffold/index'
|
21
|
+
end
|
22
|
+
|
23
|
+
def show
|
24
|
+
@record = resource
|
25
|
+
render template: 'mega_scaffold/show'
|
26
|
+
end
|
27
|
+
|
28
|
+
def new
|
29
|
+
@record = collection.new
|
30
|
+
render template: 'mega_scaffold/new'
|
31
|
+
end
|
32
|
+
|
33
|
+
def create
|
34
|
+
@record = collection.build(record_params)
|
35
|
+
if @record.save
|
36
|
+
flash[:notice] = "#{mega_scaffold.model} successfully created"
|
37
|
+
redirect_to url_for({ action: :show, mega_scaffold.pk => @record.send(mega_scaffold.pk) })
|
38
|
+
else
|
39
|
+
render template: 'mega_scaffold/new', status: :unprocessable_entity
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def edit
|
44
|
+
@record = resource
|
45
|
+
render template: 'mega_scaffold/edit'
|
46
|
+
end
|
47
|
+
|
48
|
+
def update
|
49
|
+
@record = resource
|
50
|
+
if @record.update(record_params)
|
51
|
+
flash[:notice] = "#{mega_scaffold.model} successfully updated"
|
52
|
+
redirect_to url_for({ action: :show, mega_scaffold.pk => @record.send(mega_scaffold.pk) })
|
53
|
+
else
|
54
|
+
render template: 'mega_scaffold/edit', status: :unprocessable_entity
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def destroy
|
59
|
+
@record = resource
|
60
|
+
@record.destroy
|
61
|
+
flash[:notice] = "#{mega_scaffold.model} successfully deleted"
|
62
|
+
redirect_to action: :index
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def mega_scaffold_permits
|
68
|
+
mega_scaffold.fields.map do |ee|
|
69
|
+
next if ee[:type] == :virtual
|
70
|
+
result = ee[:column].presence || ee[:name]
|
71
|
+
if result.is_a?(Hash)
|
72
|
+
if result[:permit]
|
73
|
+
{ result[:name] => result[:permit] }
|
74
|
+
else
|
75
|
+
result[:name]
|
76
|
+
end
|
77
|
+
else
|
78
|
+
result
|
79
|
+
end
|
80
|
+
end.compact
|
81
|
+
end
|
82
|
+
|
83
|
+
def record_params
|
84
|
+
params.require(mega_scaffold.model.to_s.downcase).permit(mega_scaffold_permits)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module MegaScaffold
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace MegaScaffold
|
4
|
+
|
5
|
+
initializer 'mega_scaffold.helpers', before: :load_config_initializers do
|
6
|
+
ActiveSupport.on_load :action_view do
|
7
|
+
include MegaScaffold::Helpers
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
ActionDispatch::Routing::Mapper.send :include, MegaScaffold::Routing
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MegaScaffold
|
2
|
+
class FieldsGenerator
|
3
|
+
attr_reader :model
|
4
|
+
|
5
|
+
def initialize(model)
|
6
|
+
@model = model
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate
|
10
|
+
model.columns.map do |e|
|
11
|
+
{
|
12
|
+
name: e.name,
|
13
|
+
type: MegaScaffold::TypeDetector.find_type(e),
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module MegaScaffold
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
def mega_scaffold_value(record, field, type = :show)
|
5
|
+
if field[:value].is_a?(Proc)
|
6
|
+
field[:value].call record, self
|
7
|
+
elsif field[:value].is_a?(Hash)
|
8
|
+
field[:value][type].call record, self
|
9
|
+
else
|
10
|
+
record.send(field[:name])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def mega_scaffold_field_name(field)
|
15
|
+
(field[:label].presence || field[:name]).to_s.titleize
|
16
|
+
end
|
17
|
+
|
18
|
+
def mega_scaffold_parent_url
|
19
|
+
[mega_scaffold.scope&.to_sym, @parent].reject(&:blank?)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module MegaScaffold
|
2
|
+
class KlassDecorator
|
3
|
+
attr_reader :klass, :options
|
4
|
+
|
5
|
+
def initialize(klass, options)
|
6
|
+
@klass = klass
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def decorate
|
11
|
+
# hack to have local variable available for define_method
|
12
|
+
local = options
|
13
|
+
|
14
|
+
klass.singleton_class.define_method :rules do
|
15
|
+
{
|
16
|
+
except: (Array.wrap(local[:except]) + Array.wrap(local[:ignore])).map(&:to_sym),
|
17
|
+
only: (Array.wrap(local[:only]) - Array.wrap(local[:ignore])).map(&:to_sym),
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
klass.singleton_class.define_method :fields_config do
|
22
|
+
local[:fields].map do |e|
|
23
|
+
e.merge({view: Array.wrap(e[:view]).map(&:to_sym)}).deep_symbolize_keys
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
klass.singleton_class.define_method :columns_config do
|
28
|
+
fields_config.select do |e|
|
29
|
+
next true if e[:view].empty?
|
30
|
+
next true if e[:view].include?(:index) || e[:view].include?(:all)
|
31
|
+
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
klass.singleton_class.define_method :form_config do
|
37
|
+
fields_config.select do |e|
|
38
|
+
next false if rules[:except].any? && rules[:except].include?(e[:name].to_sym)
|
39
|
+
next false if rules[:only].any? && !rules[:only].include?(e[:name].to_sym)
|
40
|
+
|
41
|
+
next true if e[:view].empty?
|
42
|
+
next true if e[:view].include?(:form) || e[:view].include?(:all)
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
klass.singleton_class.define_method :show_config do
|
48
|
+
fields_config.select do |e|
|
49
|
+
next true if e[:view].empty?
|
50
|
+
next true if e[:view].include?(:show) || e[:view].include?(:all)
|
51
|
+
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
klass.singleton_class.define_method :collection_config do
|
57
|
+
if local[:collection].is_a?(Proc)
|
58
|
+
local[:collection]
|
59
|
+
else
|
60
|
+
-> (controller) { local[:model].all }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
klass.singleton_class.define_method :parent_config do
|
65
|
+
local[:parent]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module MegaScaffold
|
2
|
+
module Routing
|
3
|
+
|
4
|
+
def mega_scaffold(*options,
|
5
|
+
fields: nil,
|
6
|
+
ignore: [:id, :created_at, :updated_at],
|
7
|
+
except: [],
|
8
|
+
only: [],
|
9
|
+
collection: nil,
|
10
|
+
parent: nil,
|
11
|
+
concerns: nil,
|
12
|
+
layout: "application"
|
13
|
+
)
|
14
|
+
|
15
|
+
model_name = options[0].to_s.singularize # user
|
16
|
+
model = model_name.classify.safe_constantize # User
|
17
|
+
concerns = Array.wrap(concerns)
|
18
|
+
return unless model
|
19
|
+
|
20
|
+
fields = MegaScaffold::FieldsGenerator.new(model).generate if fields.blank?
|
21
|
+
generator = MegaScaffold::CodeGenerator.new({
|
22
|
+
namespaces: @scope[:module].to_s.split("/").map{|e| e.classify},
|
23
|
+
concerns: concerns,
|
24
|
+
model: model,
|
25
|
+
scope: @scope,
|
26
|
+
layout: layout
|
27
|
+
})
|
28
|
+
|
29
|
+
klass = eval(generator.generate)
|
30
|
+
|
31
|
+
MegaScaffold::KlassDecorator.new(klass, {
|
32
|
+
fields: fields,
|
33
|
+
collection: collection,
|
34
|
+
parent: parent,
|
35
|
+
model: model,
|
36
|
+
except: except,
|
37
|
+
only: only,
|
38
|
+
ignore: ignore
|
39
|
+
}).decorate
|
40
|
+
|
41
|
+
resources *options
|
42
|
+
rescue ActiveRecord::StatementInvalid => ex
|
43
|
+
puts ex.message
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module MegaScaffold
|
2
|
+
class TypeDetector
|
3
|
+
def TypeDetector.find_type(field)
|
4
|
+
case field.type
|
5
|
+
when :datetime
|
6
|
+
:datetime_field
|
7
|
+
when :date
|
8
|
+
:date_field
|
9
|
+
when :text
|
10
|
+
:text_area
|
11
|
+
when :boolean
|
12
|
+
:check_box
|
13
|
+
when :integer
|
14
|
+
:number_field
|
15
|
+
else
|
16
|
+
case field.name
|
17
|
+
when /password/
|
18
|
+
:password_field
|
19
|
+
when /phone/
|
20
|
+
:phone_field
|
21
|
+
when /email/
|
22
|
+
:email_field
|
23
|
+
when /color/
|
24
|
+
:color_field
|
25
|
+
when /url/
|
26
|
+
:url_field
|
27
|
+
else
|
28
|
+
:text_field
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "mega_scaffold/version"
|
2
|
+
require "mega_scaffold/helpers"
|
3
|
+
require "mega_scaffold/controller"
|
4
|
+
require "mega_scaffold/fields_generator"
|
5
|
+
require "mega_scaffold/code_generator"
|
6
|
+
require "mega_scaffold/klass_decorator"
|
7
|
+
require "mega_scaffold/type_detector"
|
8
|
+
require "mega_scaffold/routing"
|
9
|
+
require "mega_scaffold/engine"
|
10
|
+
|
11
|
+
module MegaScaffold
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mega_scaffold
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Igor Kasyanchuk
|
8
|
+
- Liubomyr Manastyretskyi
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2022-05-05 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: puma
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: pry
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: sprockets-rails
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: country_select
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: sassc
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: kaminari
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: faker
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: carrierwave
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
version: '0'
|
133
|
+
type: :development
|
134
|
+
prerelease: false
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
- !ruby/object:Gem::Dependency
|
141
|
+
name: rspec-rails
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
type: :development
|
148
|
+
prerelease: false
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
- !ruby/object:Gem::Dependency
|
155
|
+
name: simplecov
|
156
|
+
requirement: !ruby/object:Gem::Requirement
|
157
|
+
requirements:
|
158
|
+
- - ">="
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
type: :development
|
162
|
+
prerelease: false
|
163
|
+
version_requirements: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
168
|
+
- !ruby/object:Gem::Dependency
|
169
|
+
name: wrapped_print
|
170
|
+
requirement: !ruby/object:Gem::Requirement
|
171
|
+
requirements:
|
172
|
+
- - ">="
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
type: :development
|
176
|
+
prerelease: false
|
177
|
+
version_requirements: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - ">="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
- !ruby/object:Gem::Dependency
|
183
|
+
name: ppp-util
|
184
|
+
requirement: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: '0'
|
189
|
+
type: :development
|
190
|
+
prerelease: false
|
191
|
+
version_requirements: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - ">="
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '0'
|
196
|
+
description: Fastest way to add CRUD functionality to your Rails models. No jQuery
|
197
|
+
or other frontend dependencies.
|
198
|
+
email:
|
199
|
+
- igorkasyanchuk@gmail.com
|
200
|
+
- manastyretskyi@gmail.com
|
201
|
+
executables: []
|
202
|
+
extensions: []
|
203
|
+
extra_rdoc_files: []
|
204
|
+
files:
|
205
|
+
- MIT-LICENSE
|
206
|
+
- README.md
|
207
|
+
- Rakefile
|
208
|
+
- app/views/mega_scaffold/_css.html.erb
|
209
|
+
- app/views/mega_scaffold/_form.html.erb
|
210
|
+
- app/views/mega_scaffold/edit.html.erb
|
211
|
+
- app/views/mega_scaffold/index.html.erb
|
212
|
+
- app/views/mega_scaffold/new.html.erb
|
213
|
+
- app/views/mega_scaffold/show.html.erb
|
214
|
+
- lib/mega_scaffold.rb
|
215
|
+
- lib/mega_scaffold/code_generator.rb
|
216
|
+
- lib/mega_scaffold/controller.rb
|
217
|
+
- lib/mega_scaffold/engine.rb
|
218
|
+
- lib/mega_scaffold/fields_generator.rb
|
219
|
+
- lib/mega_scaffold/helpers.rb
|
220
|
+
- lib/mega_scaffold/klass_decorator.rb
|
221
|
+
- lib/mega_scaffold/routing.rb
|
222
|
+
- lib/mega_scaffold/type_detector.rb
|
223
|
+
- lib/mega_scaffold/version.rb
|
224
|
+
homepage: https://github.com/railsjazz/mega_scaffold
|
225
|
+
licenses:
|
226
|
+
- MIT
|
227
|
+
metadata:
|
228
|
+
homepage_uri: https://www.railsjazz.com/
|
229
|
+
source_code_uri: https://github.com/railsjazz/mega_scaffold
|
230
|
+
post_install_message:
|
231
|
+
rdoc_options: []
|
232
|
+
require_paths:
|
233
|
+
- lib
|
234
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
235
|
+
requirements:
|
236
|
+
- - ">="
|
237
|
+
- !ruby/object:Gem::Version
|
238
|
+
version: '0'
|
239
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - ">="
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: '0'
|
244
|
+
requirements: []
|
245
|
+
rubygems_version: 3.2.3
|
246
|
+
signing_key:
|
247
|
+
specification_version: 4
|
248
|
+
summary: Fastest way to add CRUD functionality to your models.
|
249
|
+
test_files: []
|