railsforge 1.0.2 → 2.0.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/README.md +105 -444
- data/lib/railsforge/analyzers/controller_analyzer.rb +29 -55
- data/lib/railsforge/analyzers/database_analyzer.rb +16 -30
- data/lib/railsforge/analyzers/metrics_analyzer.rb +8 -22
- data/lib/railsforge/analyzers/model_analyzer.rb +29 -46
- data/lib/railsforge/analyzers/performance_analyzer.rb +34 -94
- data/lib/railsforge/analyzers/refactor_analyzer.rb +77 -57
- data/lib/railsforge/analyzers/security_analyzer.rb +34 -91
- data/lib/railsforge/analyzers/spec_analyzer.rb +17 -31
- data/lib/railsforge/cli.rb +14 -650
- data/lib/railsforge/cli_minimal.rb +8 -55
- data/lib/railsforge/doctor.rb +52 -225
- data/lib/railsforge/formatter.rb +102 -0
- data/lib/railsforge/issue.rb +23 -0
- data/lib/railsforge/loader.rb +4 -64
- data/lib/railsforge/version.rb +1 -1
- metadata +14 -82
- data/lib/railsforge/api_generator.rb +0 -397
- data/lib/railsforge/audit.rb +0 -289
- data/lib/railsforge/config.rb +0 -181
- data/lib/railsforge/database_analyzer.rb +0 -300
- data/lib/railsforge/feature_generator.rb +0 -560
- data/lib/railsforge/generator.rb +0 -313
- data/lib/railsforge/generators/api_generator.rb +0 -392
- data/lib/railsforge/generators/base_generator.rb +0 -75
- data/lib/railsforge/generators/demo_generator.rb +0 -307
- data/lib/railsforge/generators/devops_generator.rb +0 -287
- data/lib/railsforge/generators/form_generator.rb +0 -180
- data/lib/railsforge/generators/job_generator.rb +0 -176
- data/lib/railsforge/generators/monitoring_generator.rb +0 -134
- data/lib/railsforge/generators/policy_generator.rb +0 -220
- data/lib/railsforge/generators/presenter_generator.rb +0 -173
- data/lib/railsforge/generators/query_generator.rb +0 -174
- data/lib/railsforge/generators/serializer_generator.rb +0 -166
- data/lib/railsforge/generators/service_generator.rb +0 -122
- data/lib/railsforge/generators/stimulus_controller_generator.rb +0 -129
- data/lib/railsforge/generators/test_generator.rb +0 -289
- data/lib/railsforge/generators/view_component_generator.rb +0 -169
- data/lib/railsforge/graph.rb +0 -270
- data/lib/railsforge/mailer_generator.rb +0 -191
- data/lib/railsforge/plugins/plugin_loader.rb +0 -60
- data/lib/railsforge/plugins.rb +0 -30
- data/lib/railsforge/profiles.rb +0 -99
- data/lib/railsforge/refactor_analyzer.rb +0 -401
- data/lib/railsforge/refactor_controller.rb +0 -277
- data/lib/railsforge/refactors/refactor_controller.rb +0 -117
- data/lib/railsforge/template_loader.rb +0 -105
- data/lib/railsforge/templates/v1/form/spec_template.rb +0 -18
- data/lib/railsforge/templates/v1/form/template.rb +0 -28
- data/lib/railsforge/templates/v1/job/spec_template.rb +0 -17
- data/lib/railsforge/templates/v1/job/template.rb +0 -13
- data/lib/railsforge/templates/v1/policy/spec_template.rb +0 -41
- data/lib/railsforge/templates/v1/policy/template.rb +0 -57
- data/lib/railsforge/templates/v1/presenter/spec_template.rb +0 -12
- data/lib/railsforge/templates/v1/presenter/template.rb +0 -13
- data/lib/railsforge/templates/v1/query/spec_template.rb +0 -12
- data/lib/railsforge/templates/v1/query/template.rb +0 -16
- data/lib/railsforge/templates/v1/serializer/spec_template.rb +0 -13
- data/lib/railsforge/templates/v1/serializer/template.rb +0 -11
- data/lib/railsforge/templates/v1/service/spec_template.rb +0 -12
- data/lib/railsforge/templates/v1/service/template.rb +0 -25
- data/lib/railsforge/templates/v1/stimulus_controller/template.rb +0 -35
- data/lib/railsforge/templates/v1/view_component/template.rb +0 -24
- data/lib/railsforge/templates/v2/job/template.rb +0 -49
- data/lib/railsforge/templates/v2/query/template.rb +0 -66
- data/lib/railsforge/templates/v2/service/spec_template.rb +0 -33
- data/lib/railsforge/templates/v2/service/template.rb +0 -71
- data/lib/railsforge/templates/v3/job/template.rb +0 -72
- data/lib/railsforge/templates/v3/query/spec_template.rb +0 -54
- data/lib/railsforge/templates/v3/query/template.rb +0 -115
- data/lib/railsforge/templates/v3/service/spec_template.rb +0 -51
- data/lib/railsforge/templates/v3/service/template.rb +0 -93
- data/lib/railsforge/wizard.rb +0 -265
- data/lib/railsforge/wizard_tui.rb +0 -286
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# Query object template v2
|
|
2
|
-
# Enhanced query with chainable interface
|
|
3
|
-
class <%= class_name %>
|
|
4
|
-
attr_reader :relation
|
|
5
|
-
|
|
6
|
-
def initialize(relation = nil)
|
|
7
|
-
@relation = relation || default_relation
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
# Chainable scope methods
|
|
11
|
-
def filter_by(**conditions)
|
|
12
|
-
conditions.each do |attribute, value|
|
|
13
|
-
@relation = @relation.where(attribute => value)
|
|
14
|
-
end
|
|
15
|
-
self
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def order_by(attribute, direction = :asc)
|
|
19
|
-
@relation = @relation.order(attribute => direction)
|
|
20
|
-
self
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def limit(count)
|
|
24
|
-
@relation = @relation.limit(count)
|
|
25
|
-
self
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def offset(count)
|
|
29
|
-
@relation = @relation.offset(count)
|
|
30
|
-
self
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Execute the query
|
|
34
|
-
def call
|
|
35
|
-
@relation
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Convenience methods
|
|
39
|
-
def first
|
|
40
|
-
@relation.first
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def last
|
|
44
|
-
@relation.last
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def all
|
|
48
|
-
@relation.to_a
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def count
|
|
52
|
-
@relation.count
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def exists?
|
|
56
|
-
@relation.exists?
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
private
|
|
60
|
-
|
|
61
|
-
def default_relation
|
|
62
|
-
# TODO: Replace with actual model
|
|
63
|
-
# Example: Model.where(active: true)
|
|
64
|
-
raise NotImplementedError, "Override #default_relation in subclass"
|
|
65
|
-
end
|
|
66
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# RSpec tests for <%= class_name %>
|
|
2
|
-
# Enhanced v2 testing with better patterns
|
|
3
|
-
RSpec.describe <%= class_name %> do
|
|
4
|
-
let(:params) { {} }
|
|
5
|
-
let(:service) { described_class.new(params) }
|
|
6
|
-
|
|
7
|
-
describe "#call" do
|
|
8
|
-
context "with valid params" do
|
|
9
|
-
it "returns a successful result" do
|
|
10
|
-
result = service.call
|
|
11
|
-
expect(result.success?).to be true
|
|
12
|
-
expect(result.data).to be_a(Hash)
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
context "with invalid params" do
|
|
17
|
-
let(:params) { { invalid: true } }
|
|
18
|
-
|
|
19
|
-
it "returns a failure result" do
|
|
20
|
-
result = service.call
|
|
21
|
-
expect(result.failure?).to be true
|
|
22
|
-
expect(result.errors).to be_an(Array)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
describe ".call" do
|
|
28
|
-
it "creates and calls the service" do
|
|
29
|
-
result = described_class.call({})
|
|
30
|
-
expect(result).to be_a(Result)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
# Service class template v2
|
|
2
|
-
# Enhanced service with better practices
|
|
3
|
-
class <%= class_name %>
|
|
4
|
-
# Initialize with dependencies
|
|
5
|
-
# @param [Hash] params - Parameters for the service
|
|
6
|
-
def initialize(params = {})
|
|
7
|
-
@params = params
|
|
8
|
-
@errors = []
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
# Execute the service logic
|
|
12
|
-
# @return [Result] Result object with success/failure
|
|
13
|
-
def call
|
|
14
|
-
return failure(["Validation failed"]) unless validate?
|
|
15
|
-
|
|
16
|
-
success(execute_operation)
|
|
17
|
-
rescue StandardError => e
|
|
18
|
-
failure([e.message])
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Class-level call for convenience
|
|
22
|
-
# @param [Hash] params - Parameters for the service
|
|
23
|
-
# @return [Result] Result object
|
|
24
|
-
def self.call(params = {})
|
|
25
|
-
new(params).call
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
private
|
|
29
|
-
|
|
30
|
-
# Validate inputs
|
|
31
|
-
# @return [Boolean] True if valid
|
|
32
|
-
def validate?
|
|
33
|
-
true
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Execute the main operation
|
|
37
|
-
# @return [Object] Operation result
|
|
38
|
-
def execute_operation
|
|
39
|
-
# TODO: Implement service logic
|
|
40
|
-
{}
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Helper method for success result
|
|
44
|
-
# @param [Object] data - Result data
|
|
45
|
-
# @return [Result]
|
|
46
|
-
def success(data)
|
|
47
|
-
Result.new(success: true, data: data, errors: [])
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Helper method for failure result
|
|
51
|
-
# @param [Array<String>] errors - Error messages
|
|
52
|
-
# @return [Result]
|
|
53
|
-
def failure(errors)
|
|
54
|
-
Result.new(success: false, data: nil, errors: errors)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Result class for service responses
|
|
59
|
-
class Result
|
|
60
|
-
attr_reader :success, :data, :errors
|
|
61
|
-
|
|
62
|
-
def initialize(success:, data:, errors:)
|
|
63
|
-
@success = success
|
|
64
|
-
@data = data
|
|
65
|
-
@errors = errors
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def failure?
|
|
69
|
-
!success
|
|
70
|
-
end
|
|
71
|
-
end
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# Job template v3
|
|
2
|
-
# Advanced job with retries, priority, and callbacks
|
|
3
|
-
class <%= class_name %>Job < ApplicationJob
|
|
4
|
-
# Queue configuration
|
|
5
|
-
queue_with_priority PRIORITIES[:default]
|
|
6
|
-
|
|
7
|
-
# Retry configuration
|
|
8
|
-
retry_on StandardError, wait: :exponential_backoff, attempts: 5
|
|
9
|
-
|
|
10
|
-
# Discard configuration for permanent failures
|
|
11
|
-
discard_on PermanentJobError do |job, error|
|
|
12
|
-
Rails.logger.error("Job #{job.job_id} permanently failed: #{error.message}")
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Callback hooks
|
|
16
|
-
around_perform :instrument_job
|
|
17
|
-
before_perform :log_start
|
|
18
|
-
after_perform :log_complete
|
|
19
|
-
|
|
20
|
-
# Perform the job
|
|
21
|
-
# @param [Hash] args - Job arguments
|
|
22
|
-
def perform(*args)
|
|
23
|
-
# TODO: Implement job logic
|
|
24
|
-
# Example:
|
|
25
|
-
# user = User.find(args[:user_id])
|
|
26
|
-
# UserMailer.welcome(user).deliver_now
|
|
27
|
-
Rails.logger.info("Executing #{self.class.name}")
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Schedule for later
|
|
31
|
-
# @param [Integer] delay - Delay in seconds
|
|
32
|
-
def self.perform_in(delay, *args)
|
|
33
|
-
set(wait: delay seconds).perform_later(*args)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Schedule at specific time
|
|
37
|
-
# @param [DateTime] time - Scheduled time
|
|
38
|
-
def self.perform_at(time, *args)
|
|
39
|
-
set(wait_until: time).perform_later(*args)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Unique job - prevent duplicates
|
|
43
|
-
def self.unique_by(*attributes)
|
|
44
|
-
sidekiq_options unique: true,
|
|
45
|
-
unique_args: ->(args) { args }
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
private
|
|
49
|
-
|
|
50
|
-
# Instrument the job for monitoring
|
|
51
|
-
def instrument_job
|
|
52
|
-
start_time = Time.now
|
|
53
|
-
Rails.logger.info("Starting #{self.class.name} at #{start_time}")
|
|
54
|
-
|
|
55
|
-
begin
|
|
56
|
-
yield
|
|
57
|
-
ensure
|
|
58
|
-
duration = Time.now - start_time
|
|
59
|
-
Rails.logger.info("Completed #{self.class.name} in #{duration.round(2)}s")
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# Log job start
|
|
64
|
-
def log_start
|
|
65
|
-
Rails.logger.debug("Job #{job_id} starting")
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Log job completion
|
|
69
|
-
def log_complete
|
|
70
|
-
Rails.logger.debug("Job #{job_id} completed")
|
|
71
|
-
end
|
|
72
|
-
end
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# Query spec template v3
|
|
2
|
-
require 'rails_helper'
|
|
3
|
-
|
|
4
|
-
RSpec.describe <%= class_name %> do
|
|
5
|
-
let(:query) { described_class.new(relation) }
|
|
6
|
-
let(:relation) { double('Relation') }
|
|
7
|
-
|
|
8
|
-
describe '#where' do
|
|
9
|
-
it 'adds conditions' do
|
|
10
|
-
expect(query.where(status: 'active')).to be_a(described_class)
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
describe '#includes' do
|
|
15
|
-
it 'adds includes' do
|
|
16
|
-
expect(query.includes(:user)).to be_a(described_class)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
describe '#order' do
|
|
21
|
-
it 'adds order' do
|
|
22
|
-
expect(query.order(created_at: :desc)).to be_a(described_class)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
describe '#paginate' do
|
|
27
|
-
it 'sets limit and offset' do
|
|
28
|
-
paginated = query.paginate(page: 2, per_page: 10)
|
|
29
|
-
expect(paginated).to be_a(described_class)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
describe '#call' do
|
|
34
|
-
it 'executes the query' do
|
|
35
|
-
allow(relation).to receive_message_chain(:where, :includes, :order, :to_a).and_return([])
|
|
36
|
-
expect(query.call).to eq([])
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
describe '#count' do
|
|
41
|
-
it 'returns total count' do
|
|
42
|
-
allow(relation).to receive_message_chain(:where, :includes, :order, :count).and_return(10)
|
|
43
|
-
expect(query.count).to eq(10)
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
describe '#pagination_info' do
|
|
48
|
-
it 'returns pagination metadata' do
|
|
49
|
-
allow(relation).to receive_message_chain(:where, :includes, :order, :count).and_return(25)
|
|
50
|
-
info = query.pagination_info(page: 1, per_page: 10)
|
|
51
|
-
expect(info[:total_pages]).to eq(3)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
# Query object template v3
|
|
2
|
-
# Advanced query with pagination and caching
|
|
3
|
-
class <%= class_name %>
|
|
4
|
-
# @return [ActiveRecord::Relation] relation
|
|
5
|
-
attr_reader :relation
|
|
6
|
-
|
|
7
|
-
def initialize(relation = default_relation)
|
|
8
|
-
@relation = relation
|
|
9
|
-
@conditions = []
|
|
10
|
-
@includes = []
|
|
11
|
-
@order = []
|
|
12
|
-
@limit_value = nil
|
|
13
|
-
@offset_value = nil
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Add conditions
|
|
17
|
-
# @param [Hash] conditions - Query conditions
|
|
18
|
-
def where(conditions)
|
|
19
|
-
@conditions << conditions
|
|
20
|
-
self
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Eager load associations
|
|
24
|
-
# @param [Array<Symbol>] associations - Associations to include
|
|
25
|
-
def includes(*associations)
|
|
26
|
-
@includes << associations
|
|
27
|
-
self
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Order results
|
|
31
|
-
# @param [Hash] order_spec - Order specification
|
|
32
|
-
def order(order_spec)
|
|
33
|
-
@order << order_spec
|
|
34
|
-
self
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Paginate results
|
|
38
|
-
# @param [Integer] page - Page number (1-indexed)
|
|
39
|
-
# @param [Integer] per_page - Items per page
|
|
40
|
-
def paginate(page: 1, per_page: 25)
|
|
41
|
-
@limit_value = per_page
|
|
42
|
-
@offset_value = (page - 1) * per_page
|
|
43
|
-
self
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Cache results
|
|
47
|
-
# @param [Integer] duration - Cache duration in seconds
|
|
48
|
-
def cached(duration: 5.minutes)
|
|
49
|
-
cache_key = generate_cache_key
|
|
50
|
-
Rails.cache.fetch(cache_key, expires_in: duration) do
|
|
51
|
-
to_a
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Execute query and return results
|
|
56
|
-
def call
|
|
57
|
-
build_relation.to_a
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Get total count
|
|
61
|
-
def count
|
|
62
|
-
build_relation.count
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Pagination metadata
|
|
66
|
-
def pagination_info(page: 1, per_page: 25)
|
|
67
|
-
total = count
|
|
68
|
-
{
|
|
69
|
-
current_page: page,
|
|
70
|
-
per_page: per_page,
|
|
71
|
-
total_count: total,
|
|
72
|
-
total_pages: (total.to_f / per_page).ceil,
|
|
73
|
-
has_next_page: (page * per_page) < total,
|
|
74
|
-
has_previous_page: page > 1
|
|
75
|
-
}
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Reset to default
|
|
79
|
-
def reset
|
|
80
|
-
@conditions = []
|
|
81
|
-
@includes = []
|
|
82
|
-
@order = []
|
|
83
|
-
@limit_value = nil
|
|
84
|
-
@offset_value = nil
|
|
85
|
-
self
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
private
|
|
89
|
-
|
|
90
|
-
def default_relation
|
|
91
|
-
raise NotImplementedError, "Override default_relation in subclass"
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def build_relation
|
|
95
|
-
result = @relation
|
|
96
|
-
|
|
97
|
-
@conditions.each { |cond| result = result.where(cond) }
|
|
98
|
-
@includes.flatten.each { |inc| result = result.includes(inc) }
|
|
99
|
-
@order.each { |ord| result = result.order(ord) }
|
|
100
|
-
result = result.limit(@limit_value) if @limit_value
|
|
101
|
-
result = result.offset(@offset_value) if @offset_value
|
|
102
|
-
|
|
103
|
-
result
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def generate_cache_key
|
|
107
|
-
{
|
|
108
|
-
conditions: @conditions,
|
|
109
|
-
includes: @includes,
|
|
110
|
-
order: @order,
|
|
111
|
-
limit: @limit_value,
|
|
112
|
-
offset: @offset_value
|
|
113
|
-
}
|
|
114
|
-
end
|
|
115
|
-
end
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# Service spec template v3
|
|
2
|
-
require 'rails_helper'
|
|
3
|
-
|
|
4
|
-
RSpec.describe <%= class_name %> do
|
|
5
|
-
let(:service) { described_class.new(params) }
|
|
6
|
-
let(:params) { { name: 'Test' } }
|
|
7
|
-
|
|
8
|
-
describe '#call' do
|
|
9
|
-
context 'with valid params' do
|
|
10
|
-
it 'returns success' do
|
|
11
|
-
result = service.call
|
|
12
|
-
expect(result).to be_success
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
it 'contains data in result' do
|
|
16
|
-
result = service.call
|
|
17
|
-
expect(result.value!).to have_key(:data)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
it 'includes metadata' do
|
|
21
|
-
result = service.call
|
|
22
|
-
expect(result.value!).to have_key(:metadata)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
context 'with invalid params' do
|
|
27
|
-
let(:params) { {} }
|
|
28
|
-
|
|
29
|
-
it 'returns failure' do
|
|
30
|
-
result = service.call
|
|
31
|
-
expect(result).to be_failure
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
describe '.call' do
|
|
37
|
-
it 'works as class method' do
|
|
38
|
-
result = described_class.call(name: 'Test')
|
|
39
|
-
expect(result).to be_success
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
describe '#then' do
|
|
44
|
-
let(:next_service) { double('NextService', new: double(call: Success({}))) }
|
|
45
|
-
|
|
46
|
-
it 'chains to another service' do
|
|
47
|
-
result = service.then(next_service, extra: 'param')
|
|
48
|
-
expect(result).to be_a(Dry::Monads::Result)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
# Service class template v3
|
|
2
|
-
# Advanced service with result monad and chaining
|
|
3
|
-
class <%= class_name %>
|
|
4
|
-
include Dry::Monads[:result, :do]
|
|
5
|
-
|
|
6
|
-
# Initialize with dependencies
|
|
7
|
-
# @param [Hash] params - Parameters for the service
|
|
8
|
-
def initialize(params = {})
|
|
9
|
-
@params = params
|
|
10
|
-
@dependencies = {}
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Register a dependency
|
|
14
|
-
# @param [Symbol] key - Dependency key
|
|
15
|
-
# @param [Object] value - Dependency value
|
|
16
|
-
def register(key, value)
|
|
17
|
-
@dependencies[key] = value
|
|
18
|
-
self
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Execute the service - returns a Dry::Monads Result
|
|
22
|
-
def call
|
|
23
|
-
yield validate!
|
|
24
|
-
yield process!
|
|
25
|
-
Success(result_data)
|
|
26
|
-
rescue StandardError => e
|
|
27
|
-
Failure(error: e.message, code: :unexpected_error)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Chain another service
|
|
31
|
-
# @param [Class] service_class - Next service to call
|
|
32
|
-
# @param [Hash] params - Parameters for next service
|
|
33
|
-
def then(service_class, params = {})
|
|
34
|
-
result = call
|
|
35
|
-
return result unless result.success?
|
|
36
|
-
|
|
37
|
-
service = service_class.new(@params.merge(params)).call
|
|
38
|
-
service
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Class-level call
|
|
42
|
-
def self.call(params = {})
|
|
43
|
-
new(params).call
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
private
|
|
47
|
-
|
|
48
|
-
# Validate inputs - returns Result
|
|
49
|
-
# Override this method to add custom validation
|
|
50
|
-
# @example
|
|
51
|
-
# def validate!
|
|
52
|
-
# return Failure(errors: ['Name required']) if @params[:name].blank?
|
|
53
|
-
# Success(true)
|
|
54
|
-
# end
|
|
55
|
-
def validate!
|
|
56
|
-
Success(true)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Process the main logic - returns Result
|
|
60
|
-
# Override this method to implement your service logic
|
|
61
|
-
# @example
|
|
62
|
-
# def process!
|
|
63
|
-
# user = User.create!(@params)
|
|
64
|
-
# @result = { user: user }
|
|
65
|
-
# Success(true)
|
|
66
|
-
# end
|
|
67
|
-
def process!
|
|
68
|
-
@result = {}
|
|
69
|
-
Success(true)
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Build result data
|
|
73
|
-
def result_data
|
|
74
|
-
{
|
|
75
|
-
data: @result,
|
|
76
|
-
metadata: {
|
|
77
|
-
service: self.class.name,
|
|
78
|
-
timestamp: Time.now.utc.iso8601
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Result wrapper for success/failure
|
|
85
|
-
module Result
|
|
86
|
-
def self.success(data)
|
|
87
|
-
{ success: true, data: data }
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def self.failure(errors)
|
|
91
|
-
{ success: false, errors: errors }
|
|
92
|
-
end
|
|
93
|
-
end
|