hot-glue 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +67 -47
- data/db/migrate/20210306223305_create_ghis.rb +9 -0
- data/db/schema.rb +1 -1
- data/lib/generators/hot_glue/scaffold_generator.rb +66 -55
- data/lib/generators/hot_glue/templates/_new_form.haml +0 -3
- data/lib/generators/hot_glue/templates/_show.haml +2 -2
- data/lib/generators/hot_glue/templates/controller.rb.erb +12 -10
- data/lib/generators/hot_glue/templates/system_spec.rb.erb +109 -14
- data/lib/generators/hot_glue/templates/update.turbo_stream.haml +4 -0
- data/lib/hotglue/version.rb +1 -1
- metadata +3 -4
- data/db/migrate/20210306223305_create_hgis.rb +0 -9
- data/lib/generators/hot_glue/templates/request_spec.rb.erb +0 -110
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 88c8ae8859f5a37ed898fb4047ab2a406c5eacbb766bcbf6dc2b43f5d419b751
|
4
|
+
data.tar.gz: 8685951b462a1b9c0f3e5920b193e057a15a2d497d103bf163702dfdbfa4f89f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54f3cbd725a0ca775b688405d8f00b0aa5611a4bd2d2bbb0566bb72fcf8a1c532e3dc88684e55a0c63dd300c0f0f0f9549f5536b74fba36714f5c97a14467c18
|
7
|
+
data.tar.gz: 269fe5314a3acbc91591efea6b2f43e049244152bfb6e1c803b67c0d4b233a461cf2131fe4825a331e205c830ee7961ba78d7c6b617374291e8c9ebb63952735
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,77 +1,118 @@
|
|
1
1
|
# Hot Glue
|
2
2
|
|
3
|
-
Hot Glue is
|
3
|
+
Hot Glue is a Rails scaffold builder for the Turbo era. It is an evolution of the admin-interface style scaffolding systems of the 2010s ([activeadmin](https://github.com/activeadmin/activeadmin), [rails_admin](https://github.com/sferik/rails_admin), and [active_scaffold](https://github.com/activescaffold/active_scaffold)).
|
4
4
|
|
5
|
-
|
5
|
+
Using Turbo-Rails and Hotwire you get a lightning-fast out-of-the-box CRUD building experience. Every page displays only a list view: new and edit operations happen as 'edit-in-place', so the user never leaves the page.
|
6
6
|
|
7
|
-
It
|
7
|
+
It will read your relationships and field types to generate your code for you, leaving you with a 'sourdough starter' to work from. If you modify the generated code, you're on your own if you want to preserve your changes and also re-generate scaffold after adding fields.
|
8
8
|
|
9
|
-
|
9
|
+
By default, it generates code that gives users full control over objects they 'own' and by default it spits out functionality giving access to all fields.
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
*
|
14
|
-
*
|
15
|
-
*
|
11
|
+
Hot Glue generates functionality that's quick and dirty. It let's you be crafty. As with a real hot glue gun, use with caution.
|
12
|
+
|
13
|
+
* Build plug-and-play scaffolding for any CRUD on any object
|
14
|
+
* mixes HAML and turbo_stream responses
|
15
|
+
* Everything edits-in-place (unless you use --big-edit, then it won't)
|
16
|
+
* Automatically reads your ActiveRecord models and relationships (make them before building your scaffolding!)
|
17
|
+
* Create-read-update-delete (CRUD) with pagination (one day: sorting & searching)
|
18
|
+
* Excellent tool for prototyping and hackathons, but a knowledge of Rails is needed.
|
19
|
+
* Nest your routes model-by-model for built-in poor man's authentication. (Customers have_many :invoices; Invoices have_many :line_items; etc)
|
16
20
|
* Plays nicely with Devise, but you can implement your own current_user object instead.
|
17
|
-
*
|
18
|
-
* Create specs automatically along with the controllers
|
21
|
+
* Kaminari for pagination.
|
22
|
+
* Create specs automatically along with the controllers.
|
19
23
|
* Throw the scaffolding away when your app is ready to graduate to its next phase (or don't if you like it).
|
20
24
|
|
21
|
-
## QUICK START
|
25
|
+
## QUICK START
|
22
26
|
|
23
27
|
It's really easy to get started by following along with this blog post that creates three simple tables (User, Event, and Format).
|
24
28
|
|
25
29
|
Feel free to build your own tables when you get to the sections for building the 'Event' scaffold:
|
26
30
|
|
27
|
-
https://
|
31
|
+
https://jasonfleetwoodboldt.com/hot-glue
|
28
32
|
|
29
33
|
## HOW EASY?
|
30
34
|
|
31
|
-
|
32
35
|
```
|
33
36
|
rails generate hot_glue:scaffold Thing
|
34
37
|
```
|
35
38
|
|
39
|
+
Generate a quick scaffold to manage a table called `pronouns`
|
40
|
+
![hot-glue-3](https://user-images.githubusercontent.com/59002/116405509-bdee2f00-a7fd-11eb-9723-4c6e22f81bd3.gif)
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
Instantly get a simple CRUD interface
|
45
|
+
|
46
|
+
![hot-glue-4](https://user-images.githubusercontent.com/59002/116405517-c2b2e300-a7fd-11eb-8423-d43e3afc9fa6.gif)
|
47
|
+
|
48
|
+
|
36
49
|
## TO INSTALL
|
37
50
|
|
38
|
-
- Add
|
51
|
+
- Add `gem 'turbo-rails'` to your Gemfile & `bundle install`
|
39
52
|
|
40
53
|
- Then install it with `rails turbo:install`
|
41
54
|
|
42
55
|
- The Turbo install has switched your action cable settings from 'async' to Redis, so be sure to start a redis server
|
43
56
|
|
44
|
-
- Add `
|
57
|
+
- Add `gem 'hot-glue'` to your Gemfile & `bundle install`
|
45
58
|
|
46
|
-
-
|
59
|
+
- Run the hot-glue installation with `rails generate hot_glue:install`
|
47
60
|
|
48
61
|
- Add to your `application.html.erb`
|
49
62
|
```
|
50
63
|
<%= render partial: 'layouts/flash_notices' %>
|
51
64
|
```
|
52
65
|
|
66
|
+
- Rspec setup
|
67
|
+
add `gem 'rspec-rails'` to your gemfile inside :development and :test
|
68
|
+
add `gem 'factory_bot_rails'` to your gemfile inside :development and :test
|
69
|
+
run `rails generate rspec:install`
|
70
|
+
configure Rspec to work with Factory Bot inside of `rails_helper.rb`
|
71
|
+
```
|
72
|
+
RSpec.configure do |config|
|
73
|
+
// ... more rspec configuration (not shown)
|
74
|
+
config.include FactoryBot::Syntax::Methods
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#rspec
|
79
|
+
|
80
|
+
- for a quick Capybara login, create a support helper in `spec/support/` and log-in as your user
|
81
|
+
```
|
82
|
+
def login_as(account)
|
83
|
+
visit '/accounts/sign_in'
|
84
|
+
within("#new_account") do
|
85
|
+
fill_in 'Email', with: account.email
|
86
|
+
fill_in 'Password', with: 'password'
|
87
|
+
end
|
88
|
+
click_button 'Log in'
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
53
92
|
- Install Bootstrap (optional)
|
54
93
|
|
55
94
|
Bootstrap with Webpack:
|
56
95
|
- change `stylesheet_link_tag` to `stylesheet_pack_tag` in your application layout
|
57
|
-
- `yarn add bootstrap`
|
58
|
-
- create a new file at `app/javascript/
|
96
|
+
- run `yarn add bootstrap`
|
97
|
+
- create a new file at `app/javascript/require_bootstrap.scss` with this content
|
59
98
|
```
|
60
99
|
@import "~bootstrap/scss/bootstrap.scss";
|
61
100
|
```
|
62
101
|
|
63
102
|
- add to `app/javascript/packs/application.js`
|
64
103
|
```
|
65
|
-
import '
|
104
|
+
import 'require_bootstrap'
|
66
105
|
```
|
67
106
|
|
68
107
|
Bootstrap with Sprockets:
|
69
|
-
- use bootstrap-
|
70
|
-
|
108
|
+
- use bootstrap-rubygem gem
|
109
|
+
- see README for bootstrap-rubygem to install
|
110
|
+
|
71
111
|
|
72
112
|
- Install Devise or implement your own authentication
|
113
|
+
(or only use --gd mode, see below)
|
73
114
|
|
74
|
-
-
|
115
|
+
- font-awesome
|
75
116
|
|
76
117
|
|
77
118
|
### First Argument
|
@@ -313,6 +354,8 @@ If you do not want inline editing of your list items but instead to fall back to
|
|
313
354
|
|
314
355
|
# VERSION HISTORY
|
315
356
|
|
357
|
+
#### 2021-04-28 - v0.1.0 - Very please to introduce full behavior specs, found in specs/system/, generated by default on all build code; also many fixes involving nesting and authentication"
|
358
|
+
|
316
359
|
#### 2021-03-24 - v0.0.9 - fixes in the automatic field label detection; cleans up junk in spec output
|
317
360
|
|
318
361
|
#### 2021-03-21 - v0.0.8 - show only flag; more specific spec coverage in generator spec
|
@@ -334,20 +377,12 @@ If you do not want inline editing of your list items but instead to fall back to
|
|
334
377
|
#### 2021-02-23 - v0.0.0 - Port of my prior work from github.com/jasonfb/common_core_js
|
335
378
|
|
336
379
|
|
337
|
-
# ACKNOWLEDGEMENTS
|
338
|
-
|
339
|
-
### "POOR MAN"
|
340
|
-
|
341
|
-
I hope one day I will leave this Earth a poor man (like my code) owning only the most simple structure for the simple form of my existence. Thanks for having educated me in this wisdom goes to my former mentor [@trak3r](https://github.com/trak3r)!
|
342
|
-
|
343
380
|
|
344
381
|
|
345
382
|
|
346
383
|
# HOW THIS GEM IS TESTED
|
347
384
|
|
348
|
-
|
349
|
-
|
350
|
-
We have two kinds of "sandboxes": a DUMMY sandbox, and also a STRAWMAN sandbox
|
385
|
+
We have one kind of "sandboxes": a DUMMY sandbox
|
351
386
|
|
352
387
|
The dummy sandbox is found at `spec/dummy`
|
353
388
|
|
@@ -355,20 +390,5 @@ The dummy lives as mostly checked- into the repository, except the folders where
|
|
355
390
|
|
356
391
|
When you run the **internal specs**, which you can do **at the root of this repo** using the command `rspec`, a set of specs will run to assert the generators are erroring when they are supposed to and producing code when they are supposed to.
|
357
392
|
|
358
|
-
|
359
393
|
The DUMMY testing DOES NOT test the actual functionality of the output code (it just tests the functionality of the generation process).
|
360
394
|
|
361
|
-
For this reason, I've also added something I call STRAWMAN testing, which is a set of steps that does these things:
|
362
|
-
|
363
|
-
The Strawman isn't in the repository, but if you build it locally, it will create itself into
|
364
|
-
|
365
|
-
`spec/strawman/`
|
366
|
-
|
367
|
-
1) Builds you a strawman sandbox app from scratch, using the native `rails new`
|
368
|
-
2) Makes a few small modifications to the new app to support this gem
|
369
|
-
3) Create two nonsense tables called OmnitableA and OmnitableB. Each omnitable has one of each kind of field (integer, text, string, date, time, etc)
|
370
|
-
4) In this way, it is a "Noah's Arc" testing strategy: It will build you a fully functional app using all of the feature sets of the gem.
|
371
|
-
5) The built app itself will contain its own specs
|
372
|
-
6) You will then cd into `spec/strawman` and run `rspec`. This second test suite now runs to full test all of the *generated* code.
|
373
|
-
|
374
|
-
|
data/db/schema.rb
CHANGED
@@ -27,7 +27,7 @@ ActiveRecord::Schema.define(version: 2021_03_06_225506) do
|
|
27
27
|
t.datetime "updated_at", precision: 6, null: false
|
28
28
|
end
|
29
29
|
|
30
|
-
create_table "
|
30
|
+
create_table "ghis", force: :cascade do |t|
|
31
31
|
t.integer "def_id"
|
32
32
|
t.datetime "created_at", precision: 6, null: false
|
33
33
|
t.datetime "updated_at", precision: 6, null: false
|
@@ -3,13 +3,35 @@ require 'ffaker'
|
|
3
3
|
|
4
4
|
|
5
5
|
module HotGlue
|
6
|
-
|
7
|
-
|
8
6
|
class Error < StandardError
|
9
7
|
end
|
10
8
|
|
11
9
|
|
10
|
+
|
11
|
+
|
12
|
+
|
12
13
|
module GeneratorHelper
|
14
|
+
def derrive_reference_name thing_as_string
|
15
|
+
assoc_class = eval(thing_as_string)
|
16
|
+
|
17
|
+
if assoc_class.respond_to?("name")
|
18
|
+
display_column = "name"
|
19
|
+
elsif assoc_class.respond_to?("to_label")
|
20
|
+
display_column = "to_label"
|
21
|
+
elsif assoc_class.respond_to?("full_name")
|
22
|
+
display_column = "full_name"
|
23
|
+
elsif assoc_class.respond_to?("display_name")
|
24
|
+
display_column = "display_name"
|
25
|
+
elsif assoc_class.respond_to?("email")
|
26
|
+
display_column = "email"
|
27
|
+
else
|
28
|
+
raise("this should have been caught by the checker in the initializer")
|
29
|
+
# puts "*** Oops: Can't find any column to use as the display label for the #{assoc.name.to_s} association on the #{singular_class} model . TODO: Please implement just one of: 1) name, 2) to_label, 3) full_name, 4) display_name, or 5) email directly on your #{assoc.class_name} model (either as database field or model methods), then RERUN THIS GENERATOR. (If more than one is implemented, the field to use will be chosen based on the rank here, e.g., if name is present it will be used; if not, I will look for a to_label, etc)"
|
30
|
+
end
|
31
|
+
display_column
|
32
|
+
end
|
33
|
+
|
34
|
+
|
13
35
|
def text_area_output(col, field_length, col_identifier )
|
14
36
|
lines = field_length % 40
|
15
37
|
if lines > 5
|
@@ -82,9 +104,12 @@ module HotGlue
|
|
82
104
|
@singular = args.first.tableize.singularize # should be in form hello_world
|
83
105
|
@plural = options['plural'] || @singular + "s" # supply to override; leave blank to use default
|
84
106
|
@auth = options['auth'] || "current_user"
|
85
|
-
@auth_identifier = options['
|
86
|
-
|
107
|
+
@auth_identifier = options['auth_identifier'] || (!@auth.nil? && @auth.gsub("current_", "")) || nil
|
108
|
+
|
109
|
+
|
110
|
+
@nest = (!options['nest'].empty? && options['nest']) || nil
|
87
111
|
@namespace = options['namespace'] || nil
|
112
|
+
|
88
113
|
@singular_class = @singular.titleize.gsub(" ", "")
|
89
114
|
@exclude_fields = []
|
90
115
|
@exclude_fields += options['exclude'].split(",").collect(&:to_sym)
|
@@ -154,12 +179,25 @@ module HotGlue
|
|
154
179
|
|
155
180
|
if assoc
|
156
181
|
ownership_field = assoc.name.to_s + "_id"
|
182
|
+
elsif !@nest
|
183
|
+
exit_message = "*** Oops: It looks like is no association from current_#{@object_owner_sym} to a class called #{@singular_class}. If your user is called something else, pass with flag auth=current_X where X is the model for your users as lowercase. Also, be sure to implement current_X as a method on your controller. (If you really don't want to implement a current_X on your controller and want me to check some other method for your current user, see the section in the docs for auth_identifier.) To make a controller that can read all records, specify with --god."
|
184
|
+
|
157
185
|
else
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
186
|
+
if @god
|
187
|
+
|
188
|
+
exit_message= "*** Oops: god mode could not find the association(?). something is wrong."
|
189
|
+
else
|
190
|
+
@auth_check = "current_user"
|
191
|
+
@nested_args.each do |arg|
|
192
|
+
|
193
|
+
if !@auth_check.method("#{arg}s")
|
194
|
+
exit_message= "*** Oops: your nesting chain does not have a assocation for #{arg}s on #{@auth_check} something is wrong."
|
195
|
+
end
|
196
|
+
byebug
|
197
|
+
puts ""
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
163
201
|
raise(HotGlue::Error, exit_message)
|
164
202
|
end
|
165
203
|
end
|
@@ -197,7 +235,7 @@ module HotGlue
|
|
197
235
|
begin
|
198
236
|
eval(assoc.class_name)
|
199
237
|
rescue NameError => e
|
200
|
-
exit_message = "*** Oops: The
|
238
|
+
exit_message = "*** Oops: The model #{singular_class} is missing an association for #{assoc_name} or the model doesn't exist. TODO: Please implement a model for #{assoc_name.titlecase}; your model #{singular_class.titlecase} should have_many :#{assoc_name}s. To make a controller that can read all records, specify with --god."
|
201
239
|
raise(HotGlue::Error, exit_message)
|
202
240
|
|
203
241
|
end
|
@@ -242,15 +280,18 @@ module HotGlue
|
|
242
280
|
|
243
281
|
unless @specs_only
|
244
282
|
template "controller.rb.erb", File.join("#{'spec/dummy/' if Rails.env.test?}app/controllers#{namespace_with_dash}", "#{plural}_controller.rb")
|
245
|
-
if @namespace
|
246
|
-
|
283
|
+
if @namespace
|
284
|
+
begin
|
285
|
+
eval(controller_descends_from)
|
286
|
+
puts " skipping base controller #{controller_descends_from}"
|
287
|
+
rescue NameError => e
|
288
|
+
template "base_controller.rb.erb", File.join("#{'spec/dummy/' if Rails.env.test?}app/controllers#{namespace_with_dash}", "base_controller.rb")
|
289
|
+
end
|
247
290
|
end
|
248
291
|
end
|
249
292
|
|
250
293
|
unless @no_specs
|
251
|
-
template "
|
252
|
-
template "system_spec.rb.erb", File.join("#{'spec/dummy/' if Rails.env.test?}spec/system#{namespace_with_dash}", "#{plural}_spec.rb")
|
253
|
-
|
294
|
+
template "system_spec.rb.erb", File.join("#{'spec/dummy/' if Rails.env.test?}spec/system#{namespace_with_dash}", "#{plural}_behavior_spec.rb")
|
254
295
|
end
|
255
296
|
|
256
297
|
template "_errors.haml", File.join("#{'spec/dummy/' if Rails.env.test?}app/views#{namespace_with_dash}", "_errors.haml")
|
@@ -329,9 +370,9 @@ module HotGlue
|
|
329
370
|
end
|
330
371
|
|
331
372
|
|
332
|
-
def path_helper_full
|
333
|
-
|
334
|
-
end
|
373
|
+
# def path_helper_full
|
374
|
+
# "#{@namespace+"_" if @namespace}#{(@nested_args.join("_") + "_" if @nested_args.any?)}#{singular}_path"
|
375
|
+
# end
|
335
376
|
|
336
377
|
def path_helper_args
|
337
378
|
if @nested_args.any?
|
@@ -443,6 +484,9 @@ module HotGlue
|
|
443
484
|
end
|
444
485
|
|
445
486
|
|
487
|
+
|
488
|
+
|
489
|
+
|
446
490
|
def copy_view_files
|
447
491
|
return if @specs_only
|
448
492
|
haml_views.each do |view|
|
@@ -536,27 +580,11 @@ module HotGlue
|
|
536
580
|
exit_message= "*** Oops. on the #{singular_class} object, there doesn't seem to be an association called '#{assoc_name}'"
|
537
581
|
exit
|
538
582
|
end
|
583
|
+
display_column = derrive_reference_name(assoc.class_name)
|
539
584
|
|
540
585
|
|
541
|
-
assoc_class = eval(assoc.class_name)
|
542
|
-
|
543
|
-
if assoc_class.respond_to?("name")
|
544
|
-
display_column = "name"
|
545
|
-
elsif assoc_class.respond_to?("to_label")
|
546
|
-
display_column = "to_label"
|
547
|
-
elsif assoc_class.respond_to?("full_name")
|
548
|
-
display_column = "full_name"
|
549
|
-
elsif assoc_class.respond_to?("display_name")
|
550
|
-
display_column = "display_name"
|
551
|
-
elsif assoc_class.respond_to?("email")
|
552
|
-
display_column = "email"
|
553
|
-
else
|
554
|
-
raise("this should have been caught by the checker in the initializer")
|
555
|
-
# puts "*** Oops: Can't find any column to use as the display label for the #{assoc.name.to_s} association on the #{singular_class} model . TODO: Please implement just one of: 1) name, 2) to_label, 3) full_name, 4) display_name, or 5) email directly on your #{assoc.class_name} model (either as database field or model methods), then RERUN THIS GENERATOR. (If more than one is implemented, the field to use will be chosen based on the rank here, e.g., if name is present it will be used; if not, I will look for a to_label, etc)"
|
556
|
-
end
|
557
|
-
|
558
586
|
"#{col_identifier}{class: \"form-group \#{'alert-danger' if #{singular}.errors.details.keys.include?(:#{assoc_name.to_s})}\"}
|
559
|
-
#{col_spaces_prepend}= f.collection_select(:#{col.to_s}, #{
|
587
|
+
#{col_spaces_prepend}= f.collection_select(:#{col.to_s}, #{assoc.class_name}.all, :id, :#{display_column}, {prompt: true, selected: @#{singular}.#{col.to_s} }, class: 'form-control')
|
560
588
|
#{col_spaces_prepend}%label.small.form-text.text-muted
|
561
589
|
#{col_spaces_prepend} #{col.to_s.humanize}"
|
562
590
|
|
@@ -636,25 +664,9 @@ module HotGlue
|
|
636
664
|
exit_message = "*** Oops. on the #{singular_class} object, there doesn't seem to be an association called '#{assoc_name}'"
|
637
665
|
raise(HotGlue::Error,exit_message)
|
638
666
|
end
|
639
|
-
|
640
|
-
assoc_class = eval(assoc.class_name)
|
641
667
|
|
642
|
-
|
643
|
-
display_column = "name"
|
644
|
-
elsif assoc_class.respond_to?("to_label")
|
645
|
-
display_column = "to_label"
|
646
|
-
elsif assoc_class.respond_to?("full_name")
|
647
|
-
display_column = "full_name"
|
648
|
-
elsif assoc_class.respond_to?("display_name")
|
649
|
-
display_column = "display_name"
|
650
|
-
elsif assoc_class.respond_to?("email")
|
651
|
-
display_column = "email"
|
652
|
-
elsif assoc_class.respond_to?("number")
|
653
|
-
display_column = "number"
|
668
|
+
display_column = derrive_reference_name(assoc.class_name)
|
654
669
|
|
655
|
-
else
|
656
|
-
puts "*** Oops: Can't find any column to use as the display label for the #{assoc.name.to_s} association on the #{singular_class} model . TODO: Please implement just one of: 1) name, 2) to_label, 3) full_name, 4) display_name, 5) email, or 6) number directly on your #{assoc.class_name} model (either as database field or model methods), then RERUN THIS GENERATOR. (If more than one is implemented, the field to use will be chosen based on the rank here, e.g., if name is present it will be used; if not, I will look for a to_label, etc)"
|
657
|
-
end
|
658
670
|
|
659
671
|
"#{col_identifer}
|
660
672
|
= #{singular}.#{assoc.name.to_s}.try(:#{display_column}) || '<span class=\"content alert-danger\">MISSING</span>'.html_safe"
|
@@ -728,7 +740,7 @@ module HotGlue
|
|
728
740
|
me = eval(singular_class)
|
729
741
|
|
730
742
|
@display_class ||=
|
731
|
-
if me.column_names.include?("name")
|
743
|
+
if me.column_names.include?("name") || me.instance_methods(false).include?(:name)
|
732
744
|
# note that all class object respond_to?(:name) with the name of their own class
|
733
745
|
# this one is unique
|
734
746
|
"name"
|
@@ -742,7 +754,6 @@ module HotGlue
|
|
742
754
|
"email"
|
743
755
|
elsif me.column_names.include?("number") || me.instance_methods(false).include?(:number)
|
744
756
|
"number"
|
745
|
-
|
746
757
|
else
|
747
758
|
exit_message = "*** Oops: Can't find any column to use as the display label on #{singular_class} model . TODO: Please implement just one of: 1) name, 2) to_label, 3) full_name, 4) display_name, 5) email, or 6) number directly on your #{singular_class} model (either as database field or model methods), then RERUN THIS GENERATOR. (If more than one is implemented, the field to use will be chosen based on the rank here, e.g., if name is present it will be used; if not, I will look for a to_label, etc)"
|
748
759
|
raise(HotGlue::Error, exit_message)
|
@@ -5,6 +5,3 @@
|
|
5
5
|
= form_with model: <%= singular %>, url: <%= path_helper_plural %>(<%= nested_objects_arity %>), method: "post" do |f|
|
6
6
|
= render partial: "<%=namespace_with_slash%><%= @plural %>/form", locals: {<%= singular %>: <%= singular %>, f: f}
|
7
7
|
|
8
|
-
.row
|
9
|
-
.col-md-12
|
10
|
-
= f.submit "Save", class: "btn btn-primary pull-right"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<%= all_line_fields %>
|
2
2
|
.col
|
3
3
|
<% if destroy_action %>
|
4
|
-
= link_to "Delete <i class='fa fa-1x fa-remove'></i>".html_safe, <%=
|
4
|
+
= link_to "Delete <i class='fa fa-1x fa-remove'></i>".html_safe, <%= path_helper_singular %>(<%= path_helper_args %>), method: :delete, data: {confirm: 'Are you sure?'}, disable_with: "Loading...", class: "delete-<%= singular %>-button btn btn-primary "
|
5
5
|
<% end %>
|
6
6
|
|
7
|
-
= link_to "Edit <i class='fa fa-1x fa-list-alt'></i>".html_safe, edit_<%=
|
7
|
+
= link_to "Edit <i class='fa fa-1x fa-list-alt'></i>".html_safe, edit_<%= path_helper_singular %>(<%= path_helper_args %>), <% if @big_edit %>'data-turbo' => 'false', <% end %>disable_with: "Loading...", class: "edit-<%= singular %>-button btn btn-primary "
|
@@ -1,7 +1,6 @@
|
|
1
1
|
class <%= controller_class_name %> < <%= controller_descends_from %>
|
2
2
|
<% unless @auth_identifier == '' || @auth.nil? %>before_action :authenticate_<%= @auth_identifier %>!<% end %>
|
3
|
-
|
4
|
-
before_action :load_<%= arg %><% end %> <% end %>
|
3
|
+
|
5
4
|
before_action :load_<%= singular_name %>, only: [:show, :edit, :update, :destroy]
|
6
5
|
helper :hot_glue
|
7
6
|
include HotGlue::ControllerHelper
|
@@ -11,22 +10,23 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
|
|
11
10
|
<% end %>
|
12
11
|
|
13
12
|
<% if any_nested? %><% nest_chain = [] %> <% @nested_args.each { |arg|
|
14
|
-
this_scope = nest_chain.empty? ? "#{@auth ? auth_object : class_name}.#{arg}s" : "
|
15
|
-
nest_chain << arg %>
|
16
|
-
|
13
|
+
this_scope = nest_chain.empty? ? "#{@auth ? auth_object : class_name}.#{arg}s" : "#{nest_chain.last}.#{arg}s"
|
14
|
+
nest_chain << arg %>
|
15
|
+
def <%= arg %>
|
16
|
+
@<%= arg %> ||= <%= this_scope %>.find(params[:<%= arg %>_id])
|
17
17
|
end<% } %><% end %>
|
18
18
|
|
19
19
|
<% if !@self_auth %>
|
20
20
|
def load_<%= singular_name %>
|
21
|
-
@<%= singular_name %> = <%= object_scope %>.find(params[:id])
|
21
|
+
@<%= singular_name %> = <%= object_scope.gsub("@",'') %>.find(params[:id])
|
22
22
|
end
|
23
23
|
<% else %>
|
24
24
|
def load_<%= singular_name %>
|
25
|
-
@<%= singular_name %> = <%= auth_object %>
|
25
|
+
@<%= singular_name %> = <%= auth_object.gsub("@",'') %>
|
26
26
|
end<% end %>
|
27
27
|
|
28
28
|
def load_all_<%= plural %>
|
29
|
-
<% if !@self_auth %>@<%= plural_name %> = <%= object_scope %><% if model_has_strings? %>.where(<%=class_name %>.arel_table[:email].matches("%#{@__general_string}%"))<% end %>.page(params[:page])
|
29
|
+
<% if !@self_auth %>@<%= plural_name %> = <%= object_scope.gsub("@",'') %><% if model_has_strings? %>.where(<%=class_name %>.arel_table[:email].matches("%#{@__general_string}%"))<% end %>.page(params[:page])
|
30
30
|
<% else %>@<%= plural_name %> = [<%= auth_object %>]<% end %>
|
31
31
|
end
|
32
32
|
|
@@ -81,8 +81,10 @@ class <%= controller_class_name %> < <%= controller_descends_from %>
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def update
|
84
|
-
if
|
85
|
-
|
84
|
+
if @<%= singular_name %>.update(modify_date_inputs_on_params(<%= singular %>_params<%= @auth ? ', ' + @auth : '' %>))
|
85
|
+
flash[:notice] = "Saved #{@<%= singular %>.<%= display_class %>}"
|
86
|
+
else
|
87
|
+
flash[:alert] = "<%= singular_name.titlecase %> could not be saved."
|
86
88
|
end
|
87
89
|
respond_to do |format|
|
88
90
|
format.turbo_stream
|
@@ -1,52 +1,147 @@
|
|
1
1
|
require 'rails_helper'
|
2
2
|
|
3
3
|
describe "interaction for <%= controller_class_name %>", type: :feature do
|
4
|
-
|
5
|
-
let(:<%=
|
4
|
+
include HotGlue::ControllerHelper
|
5
|
+
<% unless @auth.nil? %>let(:<%= @auth %>) {create(:<%= @auth.gsub('current_', '') %>)}<%end%>
|
6
6
|
|
7
|
-
|
7
|
+
let!(:<%= singular %>1) {create(:<%= singular %><%= object_parent_mapping_as_argument_for_specs %> )}
|
8
|
+
let!(:<%= singular %>2) {create(:<%= singular %><%= object_parent_mapping_as_argument_for_specs %> )}
|
9
|
+
let!(:<%= singular %>3) {create(:<%= singular %><%= object_parent_mapping_as_argument_for_specs %> )}
|
8
10
|
|
9
|
-
|
11
|
+
<%= objest_nest_factory_setup %>
|
10
12
|
|
13
|
+
before(:each) do
|
14
|
+
login_as(<%= @auth %>)
|
11
15
|
end
|
12
16
|
|
13
17
|
describe "index" do
|
14
18
|
it "should show me the list" do
|
19
|
+
visit <%= path_helper_plural %>
|
20
|
+
|
21
|
+
<%=
|
22
|
+
@columns.map { |col|
|
23
|
+
type = eval("#{singular_class}.columns_hash['#{col}']").type
|
24
|
+
# limit = eval("#{singular_class}.columns_hash['#{col}']").limit
|
25
|
+
# sql_type = eval("#{singular_class}.columns_hash['#{col}']").sql_type
|
26
|
+
#
|
27
|
+
|
28
|
+
case type
|
29
|
+
when :datetime
|
30
|
+
" " + ["expect(page).to have_content(#{singular}#{rand(3)+1}.#{col}.in_time_zone(#{ @auth }.timezone).strftime('%m/%d/%Y @ %l:%M %p ').gsub(' ', ' ') + timezonize(#{ @auth }.timezone) )"].join("\n ")
|
31
|
+
|
32
|
+
else
|
33
|
+
" " + ["expect(page).to have_content(#{singular}#{rand(3)+1}.#{col})"].join("\n ")
|
15
34
|
|
16
|
-
end
|
17
35
|
end
|
18
36
|
|
19
|
-
|
20
|
-
|
37
|
+
}.join("\n")
|
38
|
+
|
39
|
+
%>
|
21
40
|
|
22
41
|
end
|
23
42
|
end
|
24
43
|
|
25
|
-
describe "create" do
|
26
|
-
it "should create a new <%= singular %>" do
|
44
|
+
describe "new & create" do
|
45
|
+
it "should create a new <%= singular.titlecase %>" do
|
46
|
+
visit <%= path_helper_plural %>
|
47
|
+
click_link "New <%= singular.titlecase %>"
|
48
|
+
expect(page).to have_selector(:xpath, './/h3[contains(., "New <%= singular.titlecase %>")]')
|
49
|
+
|
50
|
+
<%=
|
51
|
+
@columns.map { |col|
|
52
|
+
type = eval("#{singular_class}.columns_hash['#{col}']").type
|
53
|
+
# limit = eval("#{singular_class}.columns_hash['#{col}']").limit
|
54
|
+
# sql_type = eval("#{singular_class}.columns_hash['#{col}']").sql_type
|
55
|
+
#
|
56
|
+
|
57
|
+
case type
|
58
|
+
when :datetime
|
59
|
+
when :integer
|
60
|
+
" " + 'find("input#' + singular + '_' + col.to_s + '").fill_in(with: rand(10))'
|
61
|
+
else
|
62
|
+
" " + "new_#{col} = 'new_test-email@nowhere.com' \n" +
|
63
|
+
' ' + 'find("input#' + singular + '_' + col.to_s + '").fill_in(with: new_' + col.to_s + ')'
|
64
|
+
end
|
27
65
|
|
66
|
+
}.join("\n")
|
67
|
+
|
68
|
+
%>
|
69
|
+
click_button "Save"
|
70
|
+
expect(page).to have_content("Successfully created")
|
71
|
+
|
72
|
+
<%=
|
73
|
+
@columns.map { |col|
|
74
|
+
type = eval("#{singular_class}.columns_hash['#{col}']").type
|
75
|
+
|
76
|
+
case type
|
77
|
+
when :datetime
|
78
|
+
when :integer
|
79
|
+
else
|
80
|
+
"expect(page).to have_content(new_#{col})"
|
28
81
|
end
|
29
|
-
end
|
30
82
|
|
31
|
-
|
32
|
-
|
83
|
+
}.join("\n")
|
84
|
+
%>
|
33
85
|
|
34
86
|
end
|
35
87
|
end
|
36
88
|
|
37
89
|
describe "show" do
|
38
90
|
it "should return a view form" do
|
91
|
+
visit <%= path_helper_plural %>
|
39
92
|
|
40
93
|
end
|
41
94
|
end
|
42
95
|
|
43
|
-
describe "update" do
|
44
|
-
it "should
|
96
|
+
describe "edit & update" do
|
97
|
+
it "should return an editable form" do
|
98
|
+
visit <%= path_helper_plural %>
|
99
|
+
find("a.edit-<%= singular %>-button[href='/<%= namespace_with_slash %><%= plural %>/#{<%= singular %>1.id}/edit']").click
|
100
|
+
|
101
|
+
expect(page).to have_content("Editing #{<%= singular %>1.<%= derrive_reference_name(singular_class) %>}")
|
102
|
+
<%=
|
103
|
+
@columns.map { |col|
|
104
|
+
type = eval("#{singular_class}.columns_hash['#{col}']").type
|
105
|
+
|
106
|
+
case type
|
107
|
+
when :datetime
|
108
|
+
when :integer
|
109
|
+
else
|
110
|
+
" " + "new_#{col.to_s} = Faker::Name.new \n" +
|
111
|
+
|
112
|
+
' find("input[name=\'' + singular + '[' + col.to_s + ']\'").fill_in(with: new_' + col.to_s + ')'
|
113
|
+
|
114
|
+
end
|
115
|
+
}.join("\n")
|
116
|
+
%>
|
117
|
+
click_button "Save"
|
118
|
+
within("turbo-frame#<%= singular %>__#{<%= singular %>1.id} ") do
|
119
|
+
|
120
|
+
|
121
|
+
<%=
|
122
|
+
@columns.map { |col|
|
123
|
+
type = eval("#{singular_class}.columns_hash['#{col}']").type
|
124
|
+
case type
|
125
|
+
when :datetime
|
126
|
+
when :integer
|
127
|
+
else
|
128
|
+
' expect(page).to have_content(new_' + col.to_s + ')'
|
129
|
+
end
|
130
|
+
}.join("\n")
|
131
|
+
%>
|
132
|
+
|
133
|
+
end
|
45
134
|
end
|
46
135
|
end
|
47
136
|
|
48
137
|
describe "destroy" do
|
49
138
|
it "should destroy" do
|
139
|
+
visit <%= path_helper_plural %>
|
140
|
+
accept_alert do
|
141
|
+
find("a.delete-<%= singular %>-button[href='<%= namespace_with_dash %>/<%= plural %>/#{<%= singular %>1.id}']").click
|
142
|
+
end
|
143
|
+
expect(page).to_not have_content(<%= singular %>1.email)
|
144
|
+
expect(<%= singular_class %>.where(id: <%= singular %>1.id).count).to eq(0)
|
50
145
|
end
|
51
146
|
end
|
52
147
|
end
|
@@ -3,3 +3,7 @@
|
|
3
3
|
= render partial: 'line', locals: {<%= singular %>: @<%= singular %> <%= nested_assignments_with_leading_comma %> }
|
4
4
|
|
5
5
|
|
6
|
+
= turbo_stream.replace "flash_notices" do
|
7
|
+
= render partial: "layouts/flash_notices"
|
8
|
+
- if @<%= singular %>.errors.any?
|
9
|
+
= render partial: "errors", locals: {resource: @<%= singular %>}
|
data/lib/hotglue/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hot-glue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Fleetwood-Boldt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-04-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -115,7 +115,7 @@ files:
|
|
115
115
|
- config/database.yml
|
116
116
|
- db/migrate/20210306212711_create_abcs.rb
|
117
117
|
- db/migrate/20210306223300_create_defs.rb
|
118
|
-
- db/migrate/
|
118
|
+
- db/migrate/20210306223305_create_ghis.rb
|
119
119
|
- db/migrate/20210306223309_create_jkls.rb
|
120
120
|
- db/migrate/20210306223701_devise_create_users.rb
|
121
121
|
- db/migrate/20210306225506_create_xyzs.rb
|
@@ -138,7 +138,6 @@ files:
|
|
138
138
|
- lib/generators/hot_glue/templates/edit.turbo_stream.haml
|
139
139
|
- lib/generators/hot_glue/templates/index.haml
|
140
140
|
- lib/generators/hot_glue/templates/new.haml
|
141
|
-
- lib/generators/hot_glue/templates/request_spec.rb.erb
|
142
141
|
- lib/generators/hot_glue/templates/system_spec.rb.erb
|
143
142
|
- lib/generators/hot_glue/templates/update.turbo_stream.haml
|
144
143
|
- lib/hot-glue.rb
|
@@ -1,110 +0,0 @@
|
|
1
|
-
require 'rails_helper'
|
2
|
-
|
3
|
-
describe <%= controller_class_name %> do
|
4
|
-
render_views
|
5
|
-
<% unless @auth.nil? %> let(:<%= @auth %>) {create(:<%= @auth.gsub('current_', '') %>)}<%end%>
|
6
|
-
let(:<%= singular %>) {create(:<%= singular %><%= object_parent_mapping_as_argument_for_specs %> )}
|
7
|
-
|
8
|
-
<%= objest_nest_factory_setup %>
|
9
|
-
|
10
|
-
before(:each) do
|
11
|
-
@request.env["devise.mapping"] = Devise.mappings[:account]
|
12
|
-
|
13
|
-
sign_in <%= @auth %>, scope: :<%= @auth %>
|
14
|
-
end
|
15
|
-
|
16
|
-
describe "index" do
|
17
|
-
it "should respond" do
|
18
|
-
get :index, xhr: true, format: 'js', params: {
|
19
|
-
<%= objest_nest_params_by_id_for_specs %>
|
20
|
-
}
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe "new" do
|
25
|
-
it "should show form" do
|
26
|
-
get :new, xhr: true, format: 'js', params: {
|
27
|
-
<%= objest_nest_params_by_id_for_specs %>
|
28
|
-
}
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
describe "create" do
|
33
|
-
it "should create a new <%= singular %>" do
|
34
|
-
expect {
|
35
|
-
post :create, xhr: true, format: 'js', params: {
|
36
|
-
<%= (@nested_args.empty? ? "" : objest_nest_params_by_id_for_specs + ",") %>
|
37
|
-
<%= singular %>: {
|
38
|
-
<%= columns_spec_with_sample_data %>
|
39
|
-
}}
|
40
|
-
}.to change { <%= @singular_class %>.all.count }.by(1)
|
41
|
-
assert_response :ok
|
42
|
-
end
|
43
|
-
|
44
|
-
# it "should not create if there are errors" do
|
45
|
-
# post :create, xhr: true, format: 'js', params: {id: <%= singular %>.id,
|
46
|
-
# <%= singular %>: {skin_id: nil}}
|
47
|
-
#
|
48
|
-
# expect(controller).to set_flash.now[:alert].to(/Oops, your <%= singular %> could not be saved/)
|
49
|
-
# end
|
50
|
-
end
|
51
|
-
|
52
|
-
describe "edit" do
|
53
|
-
it "should return an editable form" do
|
54
|
-
get :edit, xhr: true, format: 'js', params: {
|
55
|
-
<%= (@nested_args.empty? ? "" : objest_nest_params_by_id_for_specs + ",") %>
|
56
|
-
id: <%= singular %>.id
|
57
|
-
}
|
58
|
-
assert_response :ok
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
describe "show" do
|
63
|
-
it "should return a view form" do
|
64
|
-
get :show, xhr: true, format: 'js', params: {
|
65
|
-
<%= (@nested_args.empty? ? "" : objest_nest_params_by_id_for_specs + ",") %>
|
66
|
-
id: <%= singular %>.id
|
67
|
-
}
|
68
|
-
assert_response :ok
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
describe "update" do
|
73
|
-
it "should update" do
|
74
|
-
put :update, xhr: true, format: 'js',
|
75
|
-
params: {
|
76
|
-
<%= (@nested_args.empty? ? "" : objest_nest_params_by_id_for_specs + ",") %>
|
77
|
-
id: <%= singular %>.id,
|
78
|
-
<%= singular %>: {
|
79
|
-
<%= columns_spec_with_sample_data %>
|
80
|
-
}}
|
81
|
-
|
82
|
-
assert_response :ok
|
83
|
-
end
|
84
|
-
|
85
|
-
# it "should not update if invalid" do
|
86
|
-
# put :update, xhr: true, format: 'js',
|
87
|
-
# params: {
|
88
|
-
# id: <%= singular %>.id,
|
89
|
-
# <%= singular %>: {
|
90
|
-
# <%= columns_spec_with_sample_data %>
|
91
|
-
# }}
|
92
|
-
#
|
93
|
-
# assert_response :ok
|
94
|
-
#
|
95
|
-
# expect(controller).to set_flash.now[:alert].to(/Oops, your <%= singular %> could not be saved/)
|
96
|
-
# end
|
97
|
-
end
|
98
|
-
|
99
|
-
describe "#destroy" do
|
100
|
-
it "should destroy" do
|
101
|
-
post :destroy, format: 'js', params: {
|
102
|
-
<%= (@nested_args.empty? ? "" : objest_nest_params_by_id_for_specs + ",") %>
|
103
|
-
id: <%= singular %>.id
|
104
|
-
}
|
105
|
-
assert_response :ok
|
106
|
-
expect(<%= @singular_class %>.count).to be(0)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|