mega_scaffold 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/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
|
+

|
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: []
|