rage_arch 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/LICENSE +21 -0
- data/README.md +190 -0
- data/lib/generators/rage_arch/ar_dep_generator.rb +74 -0
- data/lib/generators/rage_arch/dep_generator.rb +120 -0
- data/lib/generators/rage_arch/dep_switch_generator.rb +224 -0
- data/lib/generators/rage_arch/install_generator.rb +64 -0
- data/lib/generators/rage_arch/scaffold_generator.rb +133 -0
- data/lib/generators/rage_arch/templates/ar_dep.rb.tt +46 -0
- data/lib/generators/rage_arch/templates/dep.rb.tt +16 -0
- data/lib/generators/rage_arch/templates/rage_arch.rb.tt +15 -0
- data/lib/generators/rage_arch/templates/scaffold/api_controller.rb.tt +39 -0
- data/lib/generators/rage_arch/templates/scaffold/controller.rb.tt +56 -0
- data/lib/generators/rage_arch/templates/scaffold/create.rb.tt +14 -0
- data/lib/generators/rage_arch/templates/scaffold/destroy.rb.tt +15 -0
- data/lib/generators/rage_arch/templates/scaffold/list.rb.tt +13 -0
- data/lib/generators/rage_arch/templates/scaffold/new.rb.tt +13 -0
- data/lib/generators/rage_arch/templates/scaffold/post_repo.rb.tt +35 -0
- data/lib/generators/rage_arch/templates/scaffold/show.rb.tt +14 -0
- data/lib/generators/rage_arch/templates/scaffold/update.rb.tt +15 -0
- data/lib/generators/rage_arch/templates/use_case.rb.tt +18 -0
- data/lib/generators/rage_arch/use_case_generator.rb +33 -0
- data/lib/rage_arch/container.rb +38 -0
- data/lib/rage_arch/controller.rb +22 -0
- data/lib/rage_arch/dep.rb +9 -0
- data/lib/rage_arch/dep_scanner.rb +95 -0
- data/lib/rage_arch/deps/active_record.rb +45 -0
- data/lib/rage_arch/event_publisher.rb +59 -0
- data/lib/rage_arch/fake_event_publisher.rb +37 -0
- data/lib/rage_arch/railtie.rb +23 -0
- data/lib/rage_arch/result.rb +31 -0
- data/lib/rage_arch/rspec_matchers.rb +94 -0
- data/lib/rage_arch/use_case.rb +252 -0
- data/lib/rage_arch/version.rb +5 -0
- data/lib/rage_arch.rb +97 -0
- metadata +133 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators/base"
|
|
4
|
+
|
|
5
|
+
module RageArch
|
|
6
|
+
module Generators
|
|
7
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
desc "Create config/initializers/rage_arch.rb, app/use_cases, app/deps, and include RageArch::Controller in ApplicationController"
|
|
11
|
+
def install
|
|
12
|
+
create_initializer
|
|
13
|
+
create_directories
|
|
14
|
+
inject_controller
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def create_initializer
|
|
20
|
+
path = "config/initializers/rage_arch.rb"
|
|
21
|
+
full_path = File.join(destination_root, path)
|
|
22
|
+
if File.exist?(full_path)
|
|
23
|
+
say_status :skip, path, :yellow
|
|
24
|
+
return
|
|
25
|
+
end
|
|
26
|
+
template "rage.rb.tt", path
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create_directories
|
|
30
|
+
%w[app/use_cases app/deps].each do |dir|
|
|
31
|
+
full_path = File.join(destination_root, dir)
|
|
32
|
+
if File.directory?(full_path)
|
|
33
|
+
say_status :skip, dir, :yellow
|
|
34
|
+
next
|
|
35
|
+
end
|
|
36
|
+
empty_directory dir
|
|
37
|
+
create_file File.join(dir, ".keep"), ""
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def inject_controller
|
|
42
|
+
path = "app/controllers/application_controller.rb"
|
|
43
|
+
full_path = File.join(destination_root, path)
|
|
44
|
+
unless File.exist?(full_path)
|
|
45
|
+
say_status :skip, path, :yellow
|
|
46
|
+
return
|
|
47
|
+
end
|
|
48
|
+
content = File.read(full_path)
|
|
49
|
+
if content.include?("RageArch::Controller")
|
|
50
|
+
say_status :skip, path, :yellow
|
|
51
|
+
return
|
|
52
|
+
end
|
|
53
|
+
injection = " include RageArch::Controller\n"
|
|
54
|
+
if content =~ /(class\s+ApplicationController\s*<\s*\S+)/
|
|
55
|
+
content.sub!(Regexp.last_match(0), "#{Regexp.last_match(0)}\n#{injection.chomp}")
|
|
56
|
+
File.write(full_path, content)
|
|
57
|
+
say_status :inject, path, :green
|
|
58
|
+
else
|
|
59
|
+
say_status :skip, path, :yellow
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators/base"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module RageArch
|
|
7
|
+
module Generators
|
|
8
|
+
class ScaffoldGenerator < ::Rails::Generators::NamedBase
|
|
9
|
+
source_root File.expand_path("templates", __dir__)
|
|
10
|
+
|
|
11
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
|
12
|
+
class_option :skip_model, type: :boolean, default: false, desc: "Skip model and migration (use when model already exists)"
|
|
13
|
+
class_option :api, type: :boolean, default: false, desc: "Generate API-only controller (JSON responses, no views)"
|
|
14
|
+
|
|
15
|
+
desc "Generate a full Rage CRUD: model, migration, use cases (list/show/create/update/destroy), dep (AR), controller, and routes. With --api: JSON responses only, no views."
|
|
16
|
+
def create_all
|
|
17
|
+
create_model_and_migration
|
|
18
|
+
create_use_cases
|
|
19
|
+
create_dep
|
|
20
|
+
invoke_rails_scaffold_views
|
|
21
|
+
create_controller
|
|
22
|
+
add_route
|
|
23
|
+
inject_register_ar
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def create_model_and_migration
|
|
29
|
+
return if options[:skip_model]
|
|
30
|
+
args = [name] + attributes.map(&:to_s)
|
|
31
|
+
invoke "active_record:model", args
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def create_use_cases
|
|
35
|
+
dir = File.join("app/use_cases", plural_name)
|
|
36
|
+
empty_directory dir
|
|
37
|
+
template "scaffold/list.rb.tt", File.join(dir, "list.rb")
|
|
38
|
+
template "scaffold/show.rb.tt", File.join(dir, "show.rb")
|
|
39
|
+
template "scaffold/new.rb.tt", File.join(dir, "new.rb")
|
|
40
|
+
template "scaffold/create.rb.tt", File.join(dir, "create.rb")
|
|
41
|
+
template "scaffold/update.rb.tt", File.join(dir, "update.rb")
|
|
42
|
+
template "scaffold/destroy.rb.tt", File.join(dir, "destroy.rb")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def create_dep
|
|
46
|
+
dep_dir = File.join("app/deps", plural_name)
|
|
47
|
+
empty_directory dep_dir
|
|
48
|
+
template "scaffold/post_repo.rb.tt", File.join(dep_dir, "#{singular_name}_repo.rb")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def create_controller
|
|
52
|
+
template_name = options[:api] ? "scaffold/api_controller.rb.tt" : "scaffold/controller.rb.tt"
|
|
53
|
+
template template_name, File.join("app/controllers", "#{plural_name}_controller.rb"), force: true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Reuse Rails' scaffold_controller: with --api it generates API controller (no views); otherwise controller + views.
|
|
57
|
+
def invoke_rails_scaffold_views
|
|
58
|
+
args = [name] + attributes.map(&:to_s)
|
|
59
|
+
opts = { skip_routes: true }
|
|
60
|
+
opts[:api] = true if options[:api]
|
|
61
|
+
invoke "scaffold_controller", args, opts
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def add_route
|
|
65
|
+
route "resources :#{plural_name}"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def inject_register_ar
|
|
69
|
+
initializer_path = File.join(destination_root, "config/initializers/rage_arch.rb")
|
|
70
|
+
return unless File.exist?(initializer_path)
|
|
71
|
+
content = File.read(initializer_path)
|
|
72
|
+
return if content.include?("register_ar(:#{repo_symbol})")
|
|
73
|
+
inject_line = " Rage.register_ar(:#{repo_symbol}, #{model_class_name})\n"
|
|
74
|
+
content.sub!(/(Rails\.application\.config\.after_initialize do\s*\n)/m, "\\1#{inject_line}")
|
|
75
|
+
File.write(initializer_path, content)
|
|
76
|
+
say_status :inject, "config/initializers/rage_arch.rb (register_ar :#{repo_symbol})", :green
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def plural_name
|
|
80
|
+
name.underscore.pluralize
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def singular_name
|
|
84
|
+
name.underscore
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def model_class_name
|
|
88
|
+
name.camelize
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def module_name
|
|
92
|
+
plural_name.camelize
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def repo_symbol
|
|
96
|
+
"#{singular_name}_repo"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def repo_class_name
|
|
100
|
+
"#{module_name}::#{singular_name.camelize}Repo"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def list_symbol
|
|
104
|
+
"#{plural_name}_list"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def show_symbol
|
|
108
|
+
"#{plural_name}_show"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def create_symbol
|
|
112
|
+
"#{plural_name}_create"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def update_symbol
|
|
116
|
+
"#{plural_name}_update"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def destroy_symbol
|
|
120
|
+
"#{plural_name}_destroy"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def new_symbol
|
|
124
|
+
"#{plural_name}_new"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def attribute_names_for_permit
|
|
128
|
+
return [] if attributes.blank?
|
|
129
|
+
attributes.map { |a| a.to_s.split(":").first.to_sym }
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Dep for :<%= symbol_name %>. Wraps the <%= model_name %> Active Record model.
|
|
4
|
+
# Register in config/initializers/rage_arch.rb: Rage.register(:<%= symbol_name %>, <%= full_class_name %>.new)
|
|
5
|
+
module <%= module_name %>
|
|
6
|
+
class <%= class_name %>
|
|
7
|
+
def initialize
|
|
8
|
+
@adapter = RageArch::Deps::ActiveRecord.for(<%= model_name %>)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def build(attrs = {})
|
|
12
|
+
@adapter.build(attrs)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def find(id)
|
|
16
|
+
@adapter.find(id)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def save(record)
|
|
20
|
+
@adapter.save(record)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def update(record, attrs)
|
|
24
|
+
@adapter.update(record, attrs)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def destroy(record)
|
|
28
|
+
@adapter.destroy(record)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def list(filters: {})
|
|
32
|
+
@adapter.list(filters: filters)
|
|
33
|
+
end
|
|
34
|
+
<% if @extra_methods.any? -%>
|
|
35
|
+
|
|
36
|
+
# Extra methods detected from use cases — implement as needed
|
|
37
|
+
<% @extra_methods.each do |method_name| -%>
|
|
38
|
+
|
|
39
|
+
def <%= method_name %>(*args, **kwargs)
|
|
40
|
+
# TODO: implement (e.g. delegate to @adapter or custom logic)
|
|
41
|
+
raise NotImplementedError, "<%= full_class_name %>#<%= method_name %>"
|
|
42
|
+
end
|
|
43
|
+
<% end -%>
|
|
44
|
+
<% end -%>
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Dep for :<%= symbol_name %>.
|
|
4
|
+
# Methods detected from use cases: <%= @methods.join(', ') %>.
|
|
5
|
+
# Register in config/initializers/rage_arch.rb: Rage.register(:<%= symbol_name %>, <%= full_class_name %>.new)
|
|
6
|
+
module <%= module_name %>
|
|
7
|
+
class <%= class_name %>
|
|
8
|
+
<% @methods.each do |method_name| -%>
|
|
9
|
+
|
|
10
|
+
def <%= method_name %>(*args, **kwargs)
|
|
11
|
+
# TODO: implement
|
|
12
|
+
raise NotImplementedError, "<%= full_class_name %>#<%= method_name %>"
|
|
13
|
+
end
|
|
14
|
+
<% end -%>
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Register your app's deps here. Deps are grouped by module (e.g. app/deps/posts/post_repo.rb → Posts::PostRepo).
|
|
4
|
+
# Use Rage.register(:symbol, ClassName.new) or Rage.register_ar(:symbol, Model) for AR-backed deps.
|
|
5
|
+
|
|
6
|
+
Rails.application.config.after_initialize do
|
|
7
|
+
# Deps
|
|
8
|
+
# Rage.register(:post_repo, Posts::PostRepo.new)
|
|
9
|
+
# Rage.register_ar(:user_repo, User)
|
|
10
|
+
|
|
11
|
+
# Event publisher: use cases that declare subscribe :event_name are wired here.
|
|
12
|
+
publisher = RageArch::EventPublisher.new
|
|
13
|
+
RageArch::UseCase::Base.wire_subscriptions_to(publisher)
|
|
14
|
+
Rage.register(:event_publisher, publisher)
|
|
15
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= module_name %>Controller < ApplicationController
|
|
4
|
+
def index
|
|
5
|
+
run :<%= list_symbol %>, {},
|
|
6
|
+
success: ->(r) { render json: r.value[:<%= singular_name %>s] },
|
|
7
|
+
failure: ->(r) { render json: { errors: r.errors }, status: :unprocessable_entity }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def show
|
|
11
|
+
run :<%= show_symbol %>, { id: params[:id] },
|
|
12
|
+
success: ->(r) { render json: r.value[:<%= singular_name %>] },
|
|
13
|
+
failure: ->(r) { render json: { errors: r.errors }, status: :not_found }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def create
|
|
17
|
+
run :<%= create_symbol %>, <%= singular_name %>_params,
|
|
18
|
+
success: ->(r) { render json: r.value[:<%= singular_name %>], status: :created },
|
|
19
|
+
failure: ->(r) { render json: { errors: r.errors }, status: :unprocessable_entity }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def update
|
|
23
|
+
run :<%= update_symbol %>, <%= singular_name %>_params.merge(id: params[:id]),
|
|
24
|
+
success: ->(r) { render json: r.value[:<%= singular_name %>] },
|
|
25
|
+
failure: ->(r) { render json: { errors: r.errors }, status: :unprocessable_entity }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def destroy
|
|
29
|
+
run :<%= destroy_symbol %>, { id: params[:id] },
|
|
30
|
+
success: ->(_r) { head :no_content },
|
|
31
|
+
failure: ->(r) { render json: { errors: r.errors }, status: :not_found }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def <%= singular_name %>_params
|
|
37
|
+
params.fetch(:<%= singular_name %>, params).permit(<%= attribute_names_for_permit.map { |x| ":#{x}" }.join(", ") %>)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class <%= module_name %>Controller < ApplicationController
|
|
4
|
+
def index
|
|
5
|
+
run :<%= list_symbol %>, {},
|
|
6
|
+
success: ->(r) { @<%= plural_name %> = r.value[:<%= singular_name %>s]; render :index },
|
|
7
|
+
failure: ->(r) { redirect_to root_path, alert: r.errors.join(", ") }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def show
|
|
11
|
+
run :<%= show_symbol %>, { id: params[:id] },
|
|
12
|
+
success: ->(r) { @<%= singular_name %> = r.value[:<%= singular_name %>]; render :show },
|
|
13
|
+
failure: ->(r) { redirect_to <%= plural_name %>_path, alert: r.errors.join(", ") }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def new
|
|
17
|
+
run :<%= new_symbol %>, {},
|
|
18
|
+
success: ->(r) { @<%= singular_name %> = r.value[:<%= singular_name %>]; render :new },
|
|
19
|
+
failure: ->(r) { redirect_to <%= plural_name %>_path, alert: r.errors.join(", ") }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def create
|
|
23
|
+
run :<%= create_symbol %>, <%= singular_name %>_params,
|
|
24
|
+
success: ->(r) { redirect_to <%= singular_name %>_path(r.value[:<%= singular_name %>].id), notice: "<%= model_class_name %> was successfully created." },
|
|
25
|
+
failure: ->(r) { @<%= singular_name %> = run_result(:<%= new_symbol %>, {}).value[:<%= singular_name %>]; flash_errors(r); render :new, status: :unprocessable_entity }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def edit
|
|
29
|
+
run :<%= show_symbol %>, { id: params[:id] },
|
|
30
|
+
success: ->(r) { @<%= singular_name %> = r.value[:<%= singular_name %>]; render :edit },
|
|
31
|
+
failure: ->(r) { redirect_to <%= plural_name %>_path, alert: r.errors.join(", ") }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def update
|
|
35
|
+
run :<%= update_symbol %>, <%= singular_name %>_params.merge(id: params[:id]),
|
|
36
|
+
success: ->(r) { redirect_to <%= singular_name %>_path(params[:id]), notice: "<%= model_class_name %> was successfully updated." },
|
|
37
|
+
failure: ->(r) {
|
|
38
|
+
res = run_result(:<%= show_symbol %>, id: params[:id])
|
|
39
|
+
@<%= singular_name %> = res.success? ? res.value[:<%= singular_name %>] : nil
|
|
40
|
+
flash_errors(r)
|
|
41
|
+
render :edit, status: :unprocessable_entity
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def destroy
|
|
46
|
+
run :<%= destroy_symbol %>, { id: params[:id] },
|
|
47
|
+
success: ->(_r) { redirect_to <%= plural_name %>_path, notice: "<%= model_class_name %> was successfully destroyed." },
|
|
48
|
+
failure: ->(r) { redirect_to <%= plural_name %>_path, alert: r.errors.join(", ") }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def <%= singular_name %>_params
|
|
54
|
+
params.fetch(:<%= singular_name %>, params).permit(<%= attribute_names_for_permit.map { |x| ":#{x}" }.join(", ") %>)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module <%= module_name %>
|
|
4
|
+
class Create < RageArch::UseCase::Base
|
|
5
|
+
use_case_symbol :<%= create_symbol %>
|
|
6
|
+
deps :<%= repo_symbol %>
|
|
7
|
+
|
|
8
|
+
def call(params = {})
|
|
9
|
+
item = <%= repo_symbol %>.build(params)
|
|
10
|
+
return failure(item.errors.full_messages) unless <%= repo_symbol %>.save(item)
|
|
11
|
+
success(<%= singular_name %>: item)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module <%= module_name %>
|
|
4
|
+
class Destroy < RageArch::UseCase::Base
|
|
5
|
+
use_case_symbol :<%= destroy_symbol %>
|
|
6
|
+
deps :<%= repo_symbol %>
|
|
7
|
+
|
|
8
|
+
def call(params = {})
|
|
9
|
+
item = <%= repo_symbol %>.find(params[:id])
|
|
10
|
+
return failure(["<%= model_class_name %> not found"]) unless item
|
|
11
|
+
<%= repo_symbol %>.destroy(item)
|
|
12
|
+
success(<%= singular_name %>: item)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module <%= module_name %>
|
|
4
|
+
class List < RageArch::UseCase::Base
|
|
5
|
+
use_case_symbol :<%= list_symbol %>
|
|
6
|
+
deps :<%= repo_symbol %>
|
|
7
|
+
|
|
8
|
+
def call(_params = {})
|
|
9
|
+
items = <%= repo_symbol %>.list
|
|
10
|
+
success(<%= singular_name %>s: items)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module <%= module_name %>
|
|
4
|
+
class New < RageArch::UseCase::Base
|
|
5
|
+
use_case_symbol :<%= new_symbol %>
|
|
6
|
+
deps :<%= repo_symbol %>
|
|
7
|
+
|
|
8
|
+
def call(_params = {})
|
|
9
|
+
item = <%= repo_symbol %>.build({})
|
|
10
|
+
success(<%= singular_name %>: item)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Dep for :<%= repo_symbol %>. Wraps the <%= model_class_name %> Active Record model.
|
|
4
|
+
# Registered by rage:scaffold in config/initializers/rage_arch.rb
|
|
5
|
+
module <%= module_name %>
|
|
6
|
+
class <%= singular_name.camelize %>Repo
|
|
7
|
+
def initialize
|
|
8
|
+
@adapter = RageArch::Deps::ActiveRecord.for(<%= model_class_name %>)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def build(attrs = {})
|
|
12
|
+
@adapter.build(attrs)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def find(id)
|
|
16
|
+
@adapter.find(id)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def save(record)
|
|
20
|
+
@adapter.save(record)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def update(record, attrs)
|
|
24
|
+
@adapter.update(record, attrs)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def destroy(record)
|
|
28
|
+
@adapter.destroy(record)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def list(filters: {})
|
|
32
|
+
@adapter.list(filters: filters)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module <%= module_name %>
|
|
4
|
+
class Show < RageArch::UseCase::Base
|
|
5
|
+
use_case_symbol :<%= show_symbol %>
|
|
6
|
+
deps :<%= repo_symbol %>
|
|
7
|
+
|
|
8
|
+
def call(params = {})
|
|
9
|
+
item = <%= repo_symbol %>.find(params[:id])
|
|
10
|
+
return failure(["<%= model_class_name %> not found"]) unless item
|
|
11
|
+
success(<%= singular_name %>: item)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module <%= module_name %>
|
|
4
|
+
class Update < RageArch::UseCase::Base
|
|
5
|
+
use_case_symbol :<%= update_symbol %>
|
|
6
|
+
deps :<%= repo_symbol %>
|
|
7
|
+
|
|
8
|
+
def call(params = {})
|
|
9
|
+
item = <%= repo_symbol %>.find(params[:id])
|
|
10
|
+
return failure(["<%= model_class_name %> not found"]) unless item
|
|
11
|
+
return failure(item.errors.full_messages) unless <%= repo_symbol %>.update(item, params.except(:id))
|
|
12
|
+
success(<%= singular_name %>: item)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
<%- if class_path.any? -%>
|
|
4
|
+
module <%= class_path.map(&:camelize).join('::') %>
|
|
5
|
+
<%- end -%>
|
|
6
|
+
class <%= file_name.camelize %> < RageArch::UseCase::Base
|
|
7
|
+
use_case_symbol :<%= file_name %>
|
|
8
|
+
|
|
9
|
+
deps # e.g. :order_store, :notifications
|
|
10
|
+
|
|
11
|
+
def call(params = {})
|
|
12
|
+
# TODO: implement use case logic
|
|
13
|
+
RageArch::Result.success(params)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
<%- class_path.each do -%>
|
|
17
|
+
end
|
|
18
|
+
<%- end -%>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators/named_base"
|
|
4
|
+
|
|
5
|
+
module RageArch
|
|
6
|
+
module Generators
|
|
7
|
+
class UseCaseGenerator < ::Rails::Generators::NamedBase
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
desc "Create a use case in app/use_cases/."
|
|
11
|
+
def create_use_case
|
|
12
|
+
template "use_case.rb.tt", File.join("app/use_cases", *class_path, "#{file_name}.rb")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def class_name_without_module
|
|
16
|
+
file_name.camelize
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def symbol_name
|
|
20
|
+
file_name
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def use_case_symbol
|
|
24
|
+
":#{symbol_name}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def module_namespace
|
|
28
|
+
return "" if class_path.empty?
|
|
29
|
+
class_path.map(&:camelize).join("::") + "::"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RageArch
|
|
4
|
+
# Container to register and resolve dependencies by symbol.
|
|
5
|
+
# Usage: Rage.register(:order_store, MyAdapter.new); Rage.resolve(:order_store)
|
|
6
|
+
class Container
|
|
7
|
+
class << self
|
|
8
|
+
def register(symbol, implementation = nil, &block)
|
|
9
|
+
registry[symbol] = block || implementation
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def resolve(symbol)
|
|
13
|
+
entry = registry[symbol]
|
|
14
|
+
raise KeyError, "Dep not registered: #{symbol.inspect}" unless entry
|
|
15
|
+
|
|
16
|
+
if entry.respond_to?(:call) && entry.is_a?(Proc)
|
|
17
|
+
entry.call
|
|
18
|
+
elsif entry.is_a?(Class)
|
|
19
|
+
entry.new
|
|
20
|
+
else
|
|
21
|
+
entry
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def registered?(symbol)
|
|
26
|
+
registry.key?(symbol)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def registry
|
|
30
|
+
@registry ||= {}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def reset!
|
|
34
|
+
@registry = {}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RageArch
|
|
4
|
+
# Include in ApplicationController to get run(symbol, params, success:, failure:) and
|
|
5
|
+
# flash_errors(result). Keeps controllers thin: symbol, params, and HTTP responses only.
|
|
6
|
+
module Controller
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def run(symbol, params = {}, success:, failure:)
|
|
10
|
+
result = run_result(symbol, params)
|
|
11
|
+
(result.success? ? success : failure).call(result)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run_result(symbol, params = {})
|
|
15
|
+
RageArch::UseCase::Base.build(symbol).call(params)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def flash_errors(result)
|
|
19
|
+
flash.now[:alert] = result.errors.join(", ")
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RageArch
|
|
4
|
+
# Convention: a "dep" is any injectable external dependency (persistence, mailer, API).
|
|
5
|
+
# Register it in RageArch::Container by symbol and resolve it in the use case with dep(:symbol).
|
|
6
|
+
# No base class required; any object can be a dep.
|
|
7
|
+
module Dep
|
|
8
|
+
end
|
|
9
|
+
end
|