super 0.0.12 → 0.17.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +0 -1
  3. data/README.md +38 -46
  4. data/app/assets/javascripts/super/application.js +5747 -3803
  5. data/app/assets/stylesheets/super/application.css +114686 -71486
  6. data/app/controllers/super/application_controller.rb +44 -37
  7. data/app/controllers/super/substructure_controller.rb +276 -0
  8. data/app/helpers/super/form_builder_helper.rb +7 -0
  9. data/app/views/layouts/super/application.html.erb +4 -19
  10. data/app/views/super/application/_collection_header.html.erb +2 -2
  11. data/app/views/super/application/_display_actions.html.erb +1 -1
  12. data/app/views/super/application/_display_index.html.erb +2 -2
  13. data/app/views/super/application/_display_show.html.erb +1 -1
  14. data/app/views/super/application/_filter.html.erb +62 -2
  15. data/app/views/super/application/_form_field.html.erb +5 -0
  16. data/app/views/super/application/_layout.html.erb +1 -1
  17. data/app/views/super/application/_member_header.html.erb +2 -2
  18. data/app/views/super/application/_pagination.html.erb +1 -1
  19. data/app/views/super/application/_site_footer.html.erb +3 -0
  20. data/app/views/super/application/_site_header.html.erb +17 -0
  21. data/app/views/super/application/_sort_expression.html.erb +2 -2
  22. data/app/views/super/feather/README.md +0 -1
  23. data/frontend/super-frontend/dist/application.css +114686 -71486
  24. data/frontend/super-frontend/dist/application.js +5747 -3803
  25. data/lib/generators/super/install/install_generator.rb +0 -16
  26. data/lib/generators/super/install/templates/base_controller.rb.tt +0 -8
  27. data/lib/generators/super/resource/templates/resources_controller.rb.tt +4 -4
  28. data/lib/generators/super/webpacker/webpacker_generator.rb +9 -5
  29. data/lib/super.rb +5 -2
  30. data/lib/super/action_inquirer.rb +18 -3
  31. data/lib/super/assets.rb +44 -23
  32. data/lib/super/badge.rb +60 -0
  33. data/lib/super/cheat.rb +17 -0
  34. data/lib/super/compatibility.rb +19 -0
  35. data/lib/super/display/guesser.rb +3 -1
  36. data/lib/super/display/schema_types.rb +51 -2
  37. data/lib/super/error.rb +2 -0
  38. data/lib/super/filter.rb +1 -1
  39. data/lib/super/filter/form_object.rb +74 -48
  40. data/lib/super/filter/guesser.rb +2 -0
  41. data/lib/super/filter/operator.rb +90 -64
  42. data/lib/super/filter/schema_types.rb +63 -80
  43. data/lib/super/form/builder.rb +110 -27
  44. data/lib/super/form/field_transcript.rb +43 -0
  45. data/lib/super/form/guesser.rb +10 -1
  46. data/lib/super/form/schema_types.rb +73 -16
  47. data/lib/super/link.rb +38 -32
  48. data/lib/super/link_builder.rb +58 -0
  49. data/lib/super/navigation.rb +164 -0
  50. data/lib/super/pagination.rb +2 -44
  51. data/lib/super/reset.rb +22 -0
  52. data/lib/super/schema.rb +4 -0
  53. data/lib/super/useful/builder.rb +4 -4
  54. data/lib/super/version.rb +1 -1
  55. data/lib/tasks/super/cheat.rake +9 -0
  56. metadata +14 -19
  57. data/CONTRIBUTING.md +0 -56
  58. data/Rakefile +0 -36
  59. data/app/views/super/application/_filter_type_select.html.erb +0 -21
  60. data/app/views/super/application/_filter_type_text.html.erb +0 -18
  61. data/app/views/super/application/_filter_type_timestamp.html.erb +0 -24
  62. data/app/views/super/application/_form_field_checkbox.html.erb +0 -1
  63. data/app/views/super/application/_form_field_rich_text_area.html.erb +0 -1
  64. data/app/views/super/application/_form_field_select.html.erb +0 -1
  65. data/app/views/super/application/_form_field_text.html.erb +0 -1
  66. data/app/views/super/feather/_chevron_down.html +0 -1
  67. data/docs/cheat.md +0 -41
  68. data/lib/super/controls.rb +0 -22
  69. data/lib/super/controls/optional.rb +0 -113
  70. data/lib/super/controls/steps.rb +0 -106
  71. data/lib/super/controls/view.rb +0 -55
  72. data/lib/super/navigation/automatic.rb +0 -73
@@ -2,75 +2,78 @@
2
2
 
3
3
  module Super
4
4
  # Provides a default implementation for each of the resourceful actions
5
- class ApplicationController < ActionController::Base
5
+ class ApplicationController < SubstructureController
6
6
  include ClientError::Handling
7
7
 
8
- helper_method :action_inquirer
9
- helper_method :controls
8
+ helper_method :current_action
10
9
 
11
10
  # Displays a list of records to the user
12
11
  def index
13
- @records = controls.load_records(action: action_inquirer, params: params)
14
- @display = controls.display_schema(action: action_inquirer).apply(action: action_inquirer)
15
- @view = controls.index_view
16
- @query_form = controls.initialize_query_form(params: params, current_path: request.path)
17
- controls.initialize_filter_form(query_form: @query_form)
18
- controls.initialize_sort_form(query_form: @query_form)
19
- @records = controls.apply_queries(query_form: @query_form, records: @records)
12
+ @records = load_records
13
+ @display = display_schema.apply(action: current_action)
14
+ @view = index_view
15
+ @query_form = initialize_query_form
16
+ initialize_filter_form
17
+ initialize_sort_form
18
+ @records = apply_queries
20
19
  end
21
20
 
22
21
  # Displays a specific record to the user
23
22
  def show
24
- @record = controls.load_record(action: action_inquirer, params: params)
25
- @display = controls.display_schema(action: action_inquirer).apply(action: action_inquirer)
26
- @view = controls.show_view
23
+ @record = load_record
24
+ @display = display_schema.apply(action: current_action)
25
+ @view = show_view
27
26
  end
28
27
 
29
28
  # Displays a form to allow the user to create a new record
30
29
  def new
31
- @record = controls.build_record(action: action_inquirer)
32
- @form = controls.form_schema(action: action_inquirer)
33
- @view = controls.new_view
30
+ @record = build_record
31
+ @form = form_schema
32
+ @view = new_view
34
33
  end
35
34
 
36
35
  # Creates a record, or shows the validation errors
37
36
  def create
38
- @record = controls.build_record_with_params(action: action_inquirer, params: params)
37
+ @record = build_record
38
+ set_record_attributes
39
39
 
40
- if controls.save_record(action: action_inquirer, record: @record, params: params)
40
+ if save_record
41
41
  redirect_to polymorphic_path(Super::Link.polymorphic_parts(@record))
42
42
  else
43
- @form = controls.form_schema(action: action_inquirer_for("new"))
44
- @view = controls.new_view
43
+ @current_action = ActionInquirer.new!
44
+ @form = form_schema
45
+ @view = new_view
45
46
  render :new, status: :bad_request
46
47
  end
47
48
  end
48
49
 
49
50
  # Displays a form to allow the user to update an existing record
50
51
  def edit
51
- @record = controls.load_record(action: action_inquirer, params: params)
52
- @form = controls.form_schema(action: action_inquirer)
53
- @view = controls.edit_view
52
+ @record = load_record
53
+ @form = form_schema
54
+ @view = edit_view
54
55
  end
55
56
 
56
57
  # Updates a record, or shows validation errors
57
58
  def update
58
- @record = controls.load_record(action: action_inquirer, params: params)
59
+ @record = load_record
60
+ set_record_attributes
59
61
 
60
- if controls.update_record(action: action_inquirer, record: @record, params: params)
62
+ if save_record
61
63
  redirect_to polymorphic_path(Super::Link.polymorphic_parts(@record))
62
64
  else
63
- @form = controls.form_schema(action: action_inquirer_for("edit"))
64
- @view = controls.edit_view
65
+ @current_action = ActionInquirer.edit!
66
+ @form = form_schema
67
+ @view = edit_view
65
68
  render :edit, status: :bad_request
66
69
  end
67
70
  end
68
71
 
69
72
  # Deletes a record, or shows validation errors
70
73
  def destroy
71
- @record = controls.load_record(action: action_inquirer, params: params)
74
+ @record = load_record
72
75
 
73
- if controls.destroy_record(action: action_inquirer, record: @record, params: params)
76
+ if destroy_record
74
77
  redirect_to polymorphic_path(Super::Link.polymorphic_parts(@record))
75
78
  else
76
79
  flash.alert = "Couldn't delete record"
@@ -83,19 +86,23 @@ module Super
83
86
 
84
87
  private
85
88
 
86
- def controls
87
- @controls ||= new_controls
89
+ def current_action
90
+ @current_action ||=
91
+ ActionInquirer.new(
92
+ ActionInquirer.default_for_resources,
93
+ params[:action]
94
+ )
88
95
  end
89
96
 
90
- def action_inquirer
91
- @action_inquirer ||= action_inquirer_for(params[:action])
92
- end
93
-
94
- def action_inquirer_for(action)
95
- ActionInquirer.new(
97
+ def with_current_action(action)
98
+ original = @current_action
99
+ @current_action = ActionInquirer.new(
96
100
  ActionInquirer.default_for_resources,
97
101
  action
98
102
  )
103
+ yield
104
+ ensure
105
+ @current_action = original
99
106
  end
100
107
  end
101
108
  end
@@ -0,0 +1,276 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Super
4
+ # Various methods that determine the behavior of your controllers. These
5
+ # methods can and should be overridden.
6
+ class SubstructureController < ActionController::Base
7
+ private
8
+
9
+ helper_method def model
10
+ raise NotImplementedError
11
+ end
12
+
13
+ # This is an optional method
14
+ #
15
+ # @return [String]
16
+ helper_method def title
17
+ model.name.pluralize
18
+ end
19
+
20
+ # Configures what database records are visible on load. This is an optional
21
+ # method, it defaults to "`all`" methods
22
+ #
23
+ # @return [ActiveRecord::Relation]
24
+ helper_method def base_scope
25
+ model.all
26
+ end
27
+
28
+ # Configures the fields that are displayed on the index and show actions.
29
+ # This is a required method
30
+ #
31
+ # @return [Display]
32
+ helper_method def display_schema
33
+ Display.new do |fields, type|
34
+ Display::Guesser.new(model: model, action: current_action, fields: fields, type: type).call
35
+ end
36
+ end
37
+
38
+ # Configures the editable fields on the new and edit actions. This is a
39
+ # required method
40
+ #
41
+ # @return [Form]
42
+ helper_method def form_schema
43
+ Form.new do |fields, type|
44
+ Form::Guesser.new(model: model, fields: fields, type: type).call
45
+ end
46
+ end
47
+
48
+ # Configures which parameters could be written to the database. This is a
49
+ # required method
50
+ #
51
+ # @return [ActionController::Parameters]
52
+ helper_method def permitted_params
53
+ strong_params =
54
+ if current_action.create?
55
+ with_current_action("new") do
56
+ Super::Form::StrongParams.new(form_schema)
57
+ end
58
+ elsif current_action.update?
59
+ with_current_action("edit") do
60
+ Super::Form::StrongParams.new(form_schema)
61
+ end
62
+ else
63
+ Super::Form::StrongParams.new(form_schema)
64
+ end
65
+ params.require(strong_params.require(model)).permit(strong_params.permit)
66
+ end
67
+
68
+ # Configures the actions linked to on the index page. This is an optional
69
+ # method
70
+ #
71
+ # @return [Array<Link>]
72
+ helper_method def collection_actions
73
+ Super::Link.find_all(:new)
74
+ end
75
+
76
+ # Configures the actions linked to on the show page as well as each row of
77
+ # the table on the index page. This is an optional method
78
+ #
79
+ # Favor the `record` argument over the `@record` instance variable;
80
+ # `@record` won't always be set, notably on the index page where it's
81
+ # called on every row
82
+ #
83
+ # @return [Array<Link>]
84
+ helper_method def member_actions(record)
85
+ if current_action.show?
86
+ Super::Link.find_all(:edit, :destroy)
87
+ elsif current_action.edit?
88
+ Super::Link.find_all(:show, :destroy)
89
+ else
90
+ Super::Link.find_all(:show, :edit, :destroy)
91
+ end
92
+ end
93
+
94
+ helper_method def filters_enabled?
95
+ true
96
+ end
97
+
98
+ helper_method def filter_schema
99
+ Super::Filter.new do |fields, type|
100
+ Super::Filter::Guesser.new(model: model, fields: fields, type: type).call
101
+ end
102
+ end
103
+
104
+ helper_method def sort_enabled?
105
+ true
106
+ end
107
+
108
+ helper_method def sortable_columns
109
+ action = ActionInquirer.new(
110
+ ActionInquirer.default_for_resources,
111
+ "index"
112
+ )
113
+ attribute_names =
114
+ display_schema.each_attribute.map do |key, val|
115
+ val = val.build if val.respond_to?(:build)
116
+ key if val.real?
117
+ end
118
+
119
+ attribute_names.compact
120
+ end
121
+
122
+ helper_method def default_sort
123
+ { id: :desc }
124
+ end
125
+
126
+ # Specifies how many records to show per page
127
+ #
128
+ # @return [ActiveRecord::Relation]
129
+ helper_method def records_per_page
130
+ Super.configuration.index_records_per_page
131
+ end
132
+
133
+ def load_records
134
+ base_scope
135
+ end
136
+
137
+ def load_record
138
+ base_scope.find(params[:id])
139
+ end
140
+
141
+ def build_record
142
+ base_scope.build
143
+ end
144
+
145
+ def set_record_attributes
146
+ @record.attributes = permitted_params
147
+ end
148
+
149
+ def save_record
150
+ @record.save
151
+ end
152
+
153
+ def destroy_record
154
+ @record.destroy
155
+ end
156
+
157
+ def initialize_query_form
158
+ Super::Query::FormObject.new(
159
+ model: model,
160
+ params: params,
161
+ namespace: :q,
162
+ current_path: request.path,
163
+ )
164
+ end
165
+
166
+ def apply_queries
167
+ @query_form.apply_changes(@records)
168
+ end
169
+
170
+ def initialize_filter_form
171
+ if filters_enabled?
172
+ @query_form.add(
173
+ Super::Filter::FormObject,
174
+ namespace: :f,
175
+ schema: filter_schema
176
+ )
177
+ end
178
+ end
179
+
180
+ def initialize_sort_form
181
+ if sort_enabled?
182
+ @query_form.add(
183
+ Super::Sort::FormObject,
184
+ namespace: :s,
185
+ default: default_sort,
186
+ sortable_columns: sortable_columns
187
+ )
188
+ end
189
+ end
190
+
191
+ # Sets up pagination
192
+ #
193
+ # @return [Pagination]
194
+ def initialize_pagination
195
+ Pagination.new(
196
+ total_count: @records.size,
197
+ limit: records_per_page,
198
+ query_params: request.GET,
199
+ page_query_param: :page
200
+ )
201
+ end
202
+
203
+ # Paginates
204
+ #
205
+ # @return [ActiveRecord::Relation]
206
+ def paginate_records
207
+ @records
208
+ .limit(@pagination.limit)
209
+ .offset(@pagination.offset)
210
+ end
211
+
212
+ def index_view
213
+ Super::Layout.new(
214
+ mains: [
215
+ Super::Panel.new(
216
+ Super::Partial.new("collection_header"),
217
+ :@display
218
+ ),
219
+ ],
220
+ asides: [
221
+ :@query_form,
222
+ ]
223
+ )
224
+ end
225
+
226
+ def show_view
227
+ Super::Layout.new(
228
+ mains: [
229
+ Super::Panel.new(
230
+ Super::Partial.new("member_header"),
231
+ :@display
232
+ ),
233
+ ]
234
+ )
235
+ end
236
+
237
+ def new_view
238
+ Super::Layout.new(
239
+ mains: [
240
+ Super::Panel.new(
241
+ Super::Partial.new("collection_header"),
242
+ :@form
243
+ ),
244
+ ]
245
+ )
246
+ end
247
+
248
+ def edit_view
249
+ Super::Layout.new(
250
+ mains: [
251
+ Super::Panel.new(
252
+ Super::Partial.new("member_header"),
253
+ :@form
254
+ ),
255
+ ]
256
+ )
257
+ end
258
+
259
+ concerning :Sitewide do
260
+ included do
261
+ helper_method :site_title
262
+ helper_method :site_navigation
263
+ end
264
+
265
+ private
266
+
267
+ def site_title
268
+ Super.configuration.title
269
+ end
270
+
271
+ def site_navigation
272
+ Super::Navigation.new(&:all)
273
+ end
274
+ end
275
+ end
276
+ end
@@ -1,7 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Super
4
+ # These are form builder view helpers. They are similar to what Rails ships
5
+ # out of the box but adds some styling and functionality.
6
+ #
7
+ # These helpers are available both in Super views and in your application's
8
+ # views.
4
9
  module FormBuilderHelper
10
+ # Super's version of `#form_for`
5
11
  def super_form_for(record, options = {}, &block)
6
12
  original = ActionView::Base.field_error_proc
7
13
  ActionView::Base.field_error_proc = Form::Builder::FIELD_ERROR_PROC
@@ -12,6 +18,7 @@ module Super
12
18
  ActionView::Base.field_error_proc = original
13
19
  end
14
20
 
21
+ # Super's version of `#form_with`
15
22
  def super_form_with(**options, &block)
16
23
  original = ActionView::Base.field_error_proc
17
24
  ActionView::Base.field_error_proc = Form::Builder::FIELD_ERROR_PROC
@@ -13,11 +13,7 @@
13
13
  <% if stylesheet.handler.sprockets? %>
14
14
  <%= stylesheet_link_tag(stylesheet.path, **stylesheet.arguments) %>
15
15
  <% elsif stylesheet.handler.webpacker? %>
16
- <% if Gem::Dependency.new("webpacker", "> 5.99", "< 7").matching_specs.any? %>
17
- <%= stylesheet_packs_with_chunks_tag(stylesheet.path) %>
18
- <% else %>
19
- <%= stylesheet_pack_tag(stylesheet.path, **stylesheet.arguments) %>
20
- <% end %>
16
+ <%= stylesheet_pack_tag(stylesheet.path, **stylesheet.arguments) %>
21
17
  <% end %>
22
18
  <% end %>
23
19
 
@@ -25,23 +21,14 @@
25
21
  <% if javascript.handler.sprockets? %>
26
22
  <%= javascript_include_tag(javascript.path) %>
27
23
  <% elsif javascript.handler.webpacker? %>
28
- <% if Gem::Dependency.new("webpacker", "> 5.99", "< 7").matching_specs.any? %>
29
- <%= javascript_packs_with_chunks_tag(javascript.path) %>
30
- <% else %>
31
- <%= javascript_pack_tag(javascript.path) %>
32
- <% end %>
24
+ <%= javascript_pack_tag(javascript.path) %>
33
25
  <% end %>
34
26
  <% end %>
35
27
  </head>
36
28
 
37
29
  <body class="font-sans pb-4">
38
30
  <div class="px-4">
39
- <header class="pt-4">
40
- <h1 class="text-lg font-bold mr-6 mb-1"><%= Super.configuration.title %></h1>
41
- <% Super::Navigation::Automatic.new.each do |text, href| %>
42
- <%= link_to(text, href, class: "inline-block mr-6") %>
43
- <% end %>
44
- </header>
31
+ <%= render "site_header" %>
45
32
 
46
33
  <%= render "flash" %>
47
34
 
@@ -49,9 +36,7 @@
49
36
 
50
37
  <%= yield %>
51
38
 
52
- <div class="pt-8 text-sm text-gray-800">
53
- <%= t("super.layout.powered_by", env: Rails.env.capitalize, version: Super::VERSION) %>
54
- </div>
39
+ <%= render "site_footer" %>
55
40
  </div>
56
41
  </body>
57
42
  </html>