steel_wheel 0.6.0 → 0.7.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 +4 -4
- data/.github/workflows/main.yml +39 -0
- data/.qlty/.gitignore +7 -0
- data/.qlty/qlty.toml +82 -0
- data/.ruby-version +1 -1
- data/Gemfile +2 -1
- data/Gemfile.lock +148 -108
- data/README.md +627 -224
- data/lib/generators/steel_wheel/application_handler/templates/handler_template.rb +4 -0
- data/lib/generators/steel_wheel/form/USAGE +8 -0
- data/lib/generators/steel_wheel/form/form_generator.rb +16 -0
- data/lib/generators/steel_wheel/form/templates/form_template.rb +3 -0
- data/lib/generators/steel_wheel/handler/templates/handler_template.rb +5 -16
- data/lib/generators/steel_wheel/scaffold_controller/USAGE +14 -0
- data/lib/generators/steel_wheel/scaffold_controller/scaffold_controller_generator.rb +100 -0
- data/lib/generators/steel_wheel/scaffold_controller/templates/controller.rb +65 -0
- data/lib/generators/steel_wheel/scaffold_controller/templates/create.rb +23 -0
- data/lib/generators/steel_wheel/scaffold_controller/templates/form.tt +12 -0
- data/lib/generators/steel_wheel/scaffold_controller/templates/index.rb +17 -0
- data/lib/generators/steel_wheel/scaffold_controller/templates/model_form.rb +32 -0
- data/lib/generators/steel_wheel/scaffold_controller/templates/search_form.rb +31 -0
- data/lib/generators/steel_wheel/scaffold_controller/templates/update.rb +31 -0
- data/lib/steel_wheel/callbacks.rb +34 -0
- data/lib/steel_wheel/components.rb +54 -0
- data/lib/steel_wheel/filters.rb +36 -0
- data/lib/steel_wheel/handler.rb +67 -68
- data/lib/steel_wheel/params.rb +5 -1
- data/lib/steel_wheel/preconditions.rb +36 -0
- data/lib/steel_wheel/query/dependency_validator.rb +14 -0
- data/lib/steel_wheel/query/exists_validator.rb +15 -0
- data/lib/steel_wheel/query/verify_validator.rb +30 -0
- data/lib/steel_wheel/railtie.rb +50 -0
- data/lib/steel_wheel/shortcuts.rb +28 -0
- data/lib/steel_wheel/version.rb +1 -1
- data/lib/steel_wheel.rb +35 -6
- data/steel_wheel.gemspec +4 -4
- metadata +38 -30
- data/.github/workflows/ruby.yml +0 -43
- data/lib/generators/steel_wheel/command/USAGE +0 -8
- data/lib/generators/steel_wheel/command/command_generator.rb +0 -16
- data/lib/generators/steel_wheel/command/templates/command_template.rb +0 -5
- data/lib/generators/steel_wheel/params/USAGE +0 -8
- data/lib/generators/steel_wheel/params/params_generator.rb +0 -16
- data/lib/generators/steel_wheel/params/templates/params_template.rb +0 -5
- data/lib/generators/steel_wheel/query/USAGE +0 -8
- data/lib/generators/steel_wheel/query/query_generator.rb +0 -16
- data/lib/generators/steel_wheel/query/templates/query_template.rb +0 -5
- data/lib/steel_wheel/command.rb +0 -24
- data/lib/steel_wheel/query.rb +0 -20
- data/lib/steel_wheel/response.rb +0 -34
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SteelWheel
|
|
4
|
+
class FormGenerator < Rails::Generators::NamedBase
|
|
5
|
+
source_root File.expand_path('templates', __dir__)
|
|
6
|
+
|
|
7
|
+
def copy_files
|
|
8
|
+
if behavior == :revoke
|
|
9
|
+
template 'form_template.rb', "app/handlers/#{file_path}_form.rb"
|
|
10
|
+
elsif behavior == :invoke
|
|
11
|
+
empty_directory Pathname.new('app/handlers').join(*class_path)
|
|
12
|
+
template 'form_template.rb', "app/handlers/#{file_path}_form.rb"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -1,21 +1,10 @@
|
|
|
1
1
|
class <%= class_name %>Handler < ApplicationHandler
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
query do
|
|
8
|
-
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
command do
|
|
12
|
-
def call(*)
|
|
13
|
-
# NOOP
|
|
14
|
-
end
|
|
15
|
-
end
|
|
2
|
+
def call
|
|
3
|
+
# NOOP
|
|
16
4
|
end
|
|
17
5
|
|
|
18
|
-
def
|
|
19
|
-
|
|
6
|
+
def on_validation_success
|
|
7
|
+
# call if current_action.create?
|
|
8
|
+
# update if current_action.update?
|
|
20
9
|
end
|
|
21
10
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Description:
|
|
2
|
+
Generates controller and handler files for a resource
|
|
3
|
+
|
|
4
|
+
Example:
|
|
5
|
+
rails generate steel_wheel:scaffold_controller Thing name:string age:integer active:boolean
|
|
6
|
+
|
|
7
|
+
This will create:
|
|
8
|
+
app/controllers/things_controller.rb
|
|
9
|
+
app/handlers/things/index_handler.rb
|
|
10
|
+
app/handlers/things/create_handler.rb
|
|
11
|
+
app/handlers/things/update_handler.rb
|
|
12
|
+
app/handlers/things/model_form.rb
|
|
13
|
+
|
|
14
|
+
The generated handlers will include appropriate params definitions based on the provided attributes.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
module SteelWheel
|
|
2
|
+
class ScaffoldControllerGenerator < Rails::Generators::NamedBase
|
|
3
|
+
include Rails::Generators::ResourceHelpers
|
|
4
|
+
|
|
5
|
+
source_root File.expand_path('templates', __dir__)
|
|
6
|
+
|
|
7
|
+
check_class_collision suffix: "Controller"
|
|
8
|
+
|
|
9
|
+
class_option :helper, type: :boolean
|
|
10
|
+
class_option :orm, banner: "NAME", type: :string, required: true,
|
|
11
|
+
desc: "ORM to generate the controller for"
|
|
12
|
+
class_option :api, type: :boolean,
|
|
13
|
+
desc: "Generate API controller"
|
|
14
|
+
|
|
15
|
+
class_option :skip_routes, type: :boolean, desc: "Don't add routes to config/routes.rb."
|
|
16
|
+
|
|
17
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
|
18
|
+
|
|
19
|
+
def create_controller_files
|
|
20
|
+
template_file = options.api? ? "api_controller.rb" : "controller.rb"
|
|
21
|
+
template template_file, File.join("app/controllers", controller_class_path, "#{controller_file_name}_controller.rb")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def copy_template
|
|
25
|
+
if behavior == :revoke
|
|
26
|
+
template 'form.tt', "lib/templates/erb/scaffold/_form.html.erb.tt"
|
|
27
|
+
elsif behavior == :invoke
|
|
28
|
+
create_file "lib/templates/erb/scaffold/_form.html.erb.tt", File.read(File.join(__dir__, 'templates', 'form.tt'))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def copy_files
|
|
33
|
+
if behavior == :revoke
|
|
34
|
+
template 'index.rb', "app/handlers/#{controller_file_path}/index_handler.rb"
|
|
35
|
+
template 'create.rb', "app/handlers/#{controller_file_path}/create_handler.rb"
|
|
36
|
+
template 'update.rb', "app/handlers/#{controller_file_path}/update_handler.rb"
|
|
37
|
+
template 'model_form.rb', "app/handlers/#{controller_file_path}/model_form.rb"
|
|
38
|
+
template 'search_form.rb', "app/handlers/#{controller_file_path}/search_form.rb"
|
|
39
|
+
elsif behavior == :invoke
|
|
40
|
+
empty_directory Pathname.new('app/handlers').join(controller_file_path)
|
|
41
|
+
template 'index.rb', "app/handlers/#{controller_file_path}/index_handler.rb"
|
|
42
|
+
template 'create.rb', "app/handlers/#{controller_file_path}/create_handler.rb"
|
|
43
|
+
template 'update.rb', "app/handlers/#{controller_file_path}/update_handler.rb"
|
|
44
|
+
template 'model_form.rb', "app/handlers/#{controller_file_path}/model_form.rb"
|
|
45
|
+
template 'search_form.rb', "app/handlers/#{controller_file_path}/search_form.rb"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
hook_for :template_engine, as: :scaffold do |template_engine|
|
|
50
|
+
invoke template_engine unless options.api?
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
hook_for :resource_route, required: true do |route|
|
|
54
|
+
invoke route unless options.skip_routes?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
hook_for :test_framework, as: :scaffold
|
|
58
|
+
|
|
59
|
+
# Invoke the helper using the controller name (pluralized)
|
|
60
|
+
hook_for :helper, as: :scaffold do |invoked|
|
|
61
|
+
invoke invoked, [ controller_name ]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def convert_to_easy_params_type(attr)
|
|
67
|
+
case attr.type
|
|
68
|
+
when :attachment, :attachments then ':array, of: :string'
|
|
69
|
+
when :belongs_to, :references then :integer # Assuming these are foreign keys
|
|
70
|
+
when :boolean then :bool
|
|
71
|
+
when :date then :date
|
|
72
|
+
when :datetime, :timestamp then :datetime
|
|
73
|
+
when :decimal then :decimal
|
|
74
|
+
when :digest then :string # Assuming this is for password digests
|
|
75
|
+
when :float then :float
|
|
76
|
+
when :integer then :integer
|
|
77
|
+
when :rich_text then :string # Or consider a custom RichText type
|
|
78
|
+
when :string, :text then :string
|
|
79
|
+
when :time then :time
|
|
80
|
+
when :token then :string # Or consider a custom Token type
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def permitted_params
|
|
85
|
+
attachments, others = attributes_names.partition { |name| attachments?(name) }
|
|
86
|
+
params = others.map { |name| ":#{name}" }
|
|
87
|
+
params += attachments.map { |name| "#{name}: []" }
|
|
88
|
+
params.join(", ")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def simple_params
|
|
92
|
+
attributes.reject { |attr| attachments?(attr.name) }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def attachments?(name)
|
|
96
|
+
attribute = attributes.find { |attr| attr.name == name }
|
|
97
|
+
attribute&.attachments?
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
class <%= controller_class_name %>Controller < ApplicationController
|
|
2
|
+
before_action :authenticate_account!
|
|
3
|
+
|
|
4
|
+
# GET <%= route_url %>
|
|
5
|
+
action :index do |handler|
|
|
6
|
+
@<%= controller_file_name %> = handler.<%= controller_file_name %>
|
|
7
|
+
@form = handler.form
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# GET <%= route_url %>/1
|
|
11
|
+
action :show, handler: :update do |handler|
|
|
12
|
+
@<%= file_name %> = handler.<%= file_name %>
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# GET <%= route_url %>/new
|
|
16
|
+
action :new, handler: :create do |handler|
|
|
17
|
+
@<%= file_name %> = handler.<%= file_name %>
|
|
18
|
+
@form = handler.form
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# GET <%= route_url %>/1/edit
|
|
22
|
+
action :edit, handler: :update do |handler|
|
|
23
|
+
@<%= file_name %> = handler.<%= file_name %>
|
|
24
|
+
@form = handler.form
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# POST <%= route_url %>
|
|
28
|
+
action :create do |handler|
|
|
29
|
+
handler.success do
|
|
30
|
+
<%- if controller_class_path.empty? -%>
|
|
31
|
+
redirect_to handler.<%= file_name %>, notice: '<%= human_name %> was successfully created.'
|
|
32
|
+
<%- else -%>
|
|
33
|
+
redirect_to [<%= controller_class_path.map{|path| ":#{path}"}.join(', ') %>, handler.<%= file_name %>], notice: '<%= human_name %> was successfully created.'
|
|
34
|
+
<%- end -%>
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
handler.failure do
|
|
38
|
+
@<%= file_name %> = handler.<%= file_name %>
|
|
39
|
+
@form = handler.form
|
|
40
|
+
render :new
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# PATCH/PUT <%= route_url %>/1
|
|
45
|
+
action :update do |handler|
|
|
46
|
+
handler.success do
|
|
47
|
+
<%- if controller_class_path.empty? -%>
|
|
48
|
+
redirect_to handler.<%= file_name %>, notice: '<%= human_name %> was successfully updated.'
|
|
49
|
+
<%- else -%>
|
|
50
|
+
redirect_to [<%= controller_class_path.map{|path| ":#{path}"}.join(', ') %>, handler.<%= file_name %>], notice: '<%= human_name %> was successfully updated.'
|
|
51
|
+
<%- end -%>
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
handler.failure do
|
|
55
|
+
@form = handler.form
|
|
56
|
+
@<%= file_name %> = handler.<%= file_name %>
|
|
57
|
+
render :edit
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# DELETE <%= route_url %>/1
|
|
62
|
+
action :destroy, handler: :update do |handler|
|
|
63
|
+
redirect_to <%= index_helper %>_url, notice: '<%= human_name %> was successfully destroyed.'
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class <%= controller_class_name %>::CreateHandler < ApplicationHandler
|
|
2
|
+
form <%= controller_class_name %>::ModelForm
|
|
3
|
+
|
|
4
|
+
verify memoize def <%= file_name %>
|
|
5
|
+
<%= singular_name.camelize %>.new(form_params.to_h)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def form_attributes
|
|
9
|
+
<%- if controller_class_path.empty? -%>
|
|
10
|
+
{ model: <%= file_name %> }
|
|
11
|
+
<%- else -%>
|
|
12
|
+
{ model: [<%= controller_class_path.map{|path| ":#{path}"}.join(', ') %>, <%= file_name %>] }
|
|
13
|
+
<%- end -%>
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def on_validation_success
|
|
17
|
+
call if current_action.create?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def call
|
|
21
|
+
<%= file_name %>.save
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<%% if @errors.any? %>
|
|
2
|
+
<div style="color: red">
|
|
3
|
+
<h2><%%= pluralize(@errors.count, "error") %> prohibited this <%= singular_table_name %> from being saved:</h2>
|
|
4
|
+
|
|
5
|
+
<ul>
|
|
6
|
+
<%% @errors.each do |error| %>
|
|
7
|
+
<li><%%= error.full_message %></li>
|
|
8
|
+
<%% end %>
|
|
9
|
+
</ul>
|
|
10
|
+
</div>
|
|
11
|
+
<%% end %>
|
|
12
|
+
<%%= render @form %>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class <%= controller_class_name %>::IndexHandler < ApplicationHandler
|
|
2
|
+
def form_attributes
|
|
3
|
+
{ method: :get, action: helpers.<%= index_helper %>_path, params: form_params }
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
form <%= controller_class_name %>::SearchForm
|
|
7
|
+
<%- simple_params.each do |attribute| -%>
|
|
8
|
+
|
|
9
|
+
filter :<%= attribute.column_name %> do |scope, value|
|
|
10
|
+
scope.where(<%= attribute.column_name %>: value)
|
|
11
|
+
end
|
|
12
|
+
<%- end -%>
|
|
13
|
+
|
|
14
|
+
filterable def <%= controller_file_name %>
|
|
15
|
+
<%= singular_name.camelize %>.all
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module <%= controller_class_name %>
|
|
2
|
+
class ModelForm < ActionForm::Rails::Base
|
|
3
|
+
resource_model <%= singular_name.camelize %>
|
|
4
|
+
<%- simple_params.each do |attribute| -%>
|
|
5
|
+
|
|
6
|
+
element :<%= attribute.column_name %> do
|
|
7
|
+
<%- if attribute.type == :boolean -%>
|
|
8
|
+
input(type: :checkbox)
|
|
9
|
+
output(type: :boolean, presence: true)
|
|
10
|
+
<%- elsif attribute.type == :text -%>
|
|
11
|
+
input(type: :textarea)
|
|
12
|
+
output(type: :string, presence: true)
|
|
13
|
+
<%- elsif attribute.type == :decimal || attribute.type == :float -%>
|
|
14
|
+
input(type: :number, step: 'any')
|
|
15
|
+
output(type: :decimal, presence: true)
|
|
16
|
+
<%- elsif attribute.type == :integer -%>
|
|
17
|
+
input(type: :number)
|
|
18
|
+
output(type: :integer, presence: true)
|
|
19
|
+
<%- elsif attribute.type == :date -%>
|
|
20
|
+
input(type: :date)
|
|
21
|
+
output(type: :date, presence: true)
|
|
22
|
+
<%- elsif attribute.type == :datetime || attribute.type == :timestamp -%>
|
|
23
|
+
input(type: :datetime_local)
|
|
24
|
+
output(type: :datetime, presence: true)
|
|
25
|
+
<%- else -%>
|
|
26
|
+
input(type: :text)
|
|
27
|
+
output(type: :string, presence: true)
|
|
28
|
+
<%- end -%>
|
|
29
|
+
end
|
|
30
|
+
<%- end -%>
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module <%= controller_class_name %>
|
|
2
|
+
class SearchForm < ActionForm::Base
|
|
3
|
+
<%- simple_params.each do |attribute| -%>
|
|
4
|
+
|
|
5
|
+
element :<%= attribute.column_name %> do
|
|
6
|
+
<%- if attribute.type == :boolean -%>
|
|
7
|
+
input(type: :checkbox)
|
|
8
|
+
output(type: :boolean)
|
|
9
|
+
<%- elsif attribute.type == :text -%>
|
|
10
|
+
input(type: :textarea)
|
|
11
|
+
output(type: :string)
|
|
12
|
+
<%- elsif attribute.type == :decimal || attribute.type == :float -%>
|
|
13
|
+
input(type: :number, step: 'any')
|
|
14
|
+
output(type: :decimal)
|
|
15
|
+
<%- elsif attribute.type == :integer -%>
|
|
16
|
+
input(type: :number)
|
|
17
|
+
output(type: :integer)
|
|
18
|
+
<%- elsif attribute.type == :date -%>
|
|
19
|
+
input(type: :date)
|
|
20
|
+
output(type: :date)
|
|
21
|
+
<%- elsif attribute.type == :datetime || attribute.type == :timestamp -%>
|
|
22
|
+
input(type: :datetime_local)
|
|
23
|
+
output(type: :datetime)
|
|
24
|
+
<%- else -%>
|
|
25
|
+
input(type: :text)
|
|
26
|
+
output(type: :string)
|
|
27
|
+
<%- end -%>
|
|
28
|
+
end
|
|
29
|
+
<%- end -%>
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class <%= controller_class_name %>::UpdateHandler < ApplicationHandler
|
|
2
|
+
form <%= controller_class_name %>::ModelForm
|
|
3
|
+
|
|
4
|
+
url_params do
|
|
5
|
+
integer :id, presence: true
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
finder :<%= file_name %>, -> { <%= singular_name.camelize %>.find_by(id: url_params.id) }, validate_existence: true
|
|
9
|
+
|
|
10
|
+
def on_validation_success
|
|
11
|
+
call if current_action.update?
|
|
12
|
+
destroy if current_action.destroy?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call
|
|
16
|
+
<%= file_name %>.update(form_params.to_h)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def destroy
|
|
20
|
+
<%= file_name %>.destroy
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def form_attributes
|
|
24
|
+
<%- if controller_class_path.empty? -%>
|
|
25
|
+
{ model: <%= file_name %> }
|
|
26
|
+
<%- else -%>
|
|
27
|
+
{ model: [<%= controller_class_path.map{|path| ":#{path}"}.join(', ') %>, <%= file_name %>] }
|
|
28
|
+
<%- end -%>
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SteelWheel
|
|
4
|
+
# Provides callbacks functionality for handlers
|
|
5
|
+
module Callbacks
|
|
6
|
+
NOOP = ->(o) { o }.freeze
|
|
7
|
+
|
|
8
|
+
def callbacks
|
|
9
|
+
@callbacks ||= {}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def failure(*statuses, &block)
|
|
13
|
+
return callbacks[:unprocessable_entity] = block if statuses.empty?
|
|
14
|
+
|
|
15
|
+
statuses.each do |status|
|
|
16
|
+
callbacks[status] = block
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def success(&block)
|
|
21
|
+
callbacks[:success] = block
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def success_callback
|
|
27
|
+
callbacks.fetch(:success, NOOP).call(self)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def failure_callback
|
|
31
|
+
callbacks.fetch(http_status, NOOP).call(self)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SteelWheel
|
|
4
|
+
# Provides parameter and form components
|
|
5
|
+
module Components
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.extend(ClassMethods)
|
|
8
|
+
base.include(InstanceMethods)
|
|
9
|
+
base.singleton_class.attr_writer :url_params_definition, :form_definition
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module ClassMethods # rubocop:disable Style/Documentation
|
|
13
|
+
def url_params_definition
|
|
14
|
+
@url_params_definition ||= Class.new(SteelWheel::Params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def form_definition
|
|
18
|
+
@form_definition ||= Class.new(ActionForm::Rails::Base)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def url_params(klass = nil, &block)
|
|
22
|
+
self.url_params_definition = Class.new(klass) if klass
|
|
23
|
+
url_params_definition.class_exec(self, &block) if block
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def form(klass = nil, &block)
|
|
27
|
+
self.form_definition = Class.new(klass) if klass
|
|
28
|
+
form_definition.class_eval(&block) if block
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
module InstanceMethods # rubocop:disable Style/Documentation
|
|
33
|
+
def url_params
|
|
34
|
+
@url_params ||= self.class.url_params_definition.new(input)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def form_params
|
|
38
|
+
@form_params ||= if @form_scope
|
|
39
|
+
self.class.form_definition.params_definition.schema[@form_scope].class.new(form_input)
|
|
40
|
+
else
|
|
41
|
+
self.class.form_definition.params_definition.new(form_input)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def form(attrs = form_attributes)
|
|
46
|
+
@form ||= self.class.form_definition.new(**attrs)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def form_attributes
|
|
50
|
+
raise SteelWheel::FormAttributesNotImplementedError
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SteelWheel
|
|
4
|
+
# Provides filtering functionality for handlers
|
|
5
|
+
module Filters
|
|
6
|
+
def self.included(base)
|
|
7
|
+
base.extend(ClassMethods)
|
|
8
|
+
base.include(InstanceMethods)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods # rubocop:disable Style/Documentation
|
|
12
|
+
def filter(name, &definition)
|
|
13
|
+
define_method("filter_by_#{name}", &definition)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def filterable(name)
|
|
17
|
+
alias_method :"initial_#{name}_scope", name
|
|
18
|
+
define_method(name) do
|
|
19
|
+
apply_filters(send(:"initial_#{name}_scope"), form_params.to_h)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module InstanceMethods # rubocop:disable Style/Documentation
|
|
25
|
+
def apply_filters(scope, search_params)
|
|
26
|
+
search_params.each do |key, value|
|
|
27
|
+
filter_method = "filter_by_#{key}"
|
|
28
|
+
raise SteelWheel::FilterNotImplementedError, key unless respond_to?(filter_method)
|
|
29
|
+
|
|
30
|
+
scope = send(filter_method, scope, value) if value.present?
|
|
31
|
+
end
|
|
32
|
+
scope
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|