auxiliary_rails 0.3.1 → 0.4.1
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 +32 -0
- data/.github/workflows/publish-gpr.yml +32 -0
- data/.github/workflows/publish-rubygems.yml +31 -0
- data/.rubocop.yml +10 -6
- data/.rubocop_todo.yml +1 -7
- data/.ruby-version +1 -1
- data/CHANGELOG.md +11 -1
- data/CODE_OF_CONDUCT.md +128 -0
- data/Gemfile +9 -1
- data/Gemfile.lock +175 -138
- data/README.md +77 -17
- data/auxiliary_rails.gemspec +12 -15
- data/bin/rspec +29 -0
- data/lib/auxiliary_rails/application/command.rb +3 -6
- data/lib/auxiliary_rails/application/error.rb +44 -4
- data/lib/auxiliary_rails/application/form.rb +1 -0
- data/lib/auxiliary_rails/application/query.rb +11 -7
- data/lib/auxiliary_rails/application/service.rb +42 -0
- data/lib/auxiliary_rails/concerns/callable.rb +23 -0
- data/lib/auxiliary_rails/concerns/errorable.rb +12 -12
- data/lib/auxiliary_rails/concerns/performable.rb +14 -22
- data/lib/auxiliary_rails/railtie.rb +2 -1
- data/lib/auxiliary_rails/version.rb +1 -1
- data/lib/auxiliary_rails/view_helpers/display_helper.rb +30 -0
- data/lib/auxiliary_rails/view_helpers.rb +4 -0
- data/lib/auxiliary_rails.rb +3 -1
- data/lib/generators/auxiliary_rails/install_errors_controller_generator.rb +31 -0
- data/lib/generators/auxiliary_rails/install_generator.rb +1 -0
- data/lib/generators/auxiliary_rails/service_generator.rb +48 -0
- data/lib/generators/auxiliary_rails/templates/commands/command_spec_template.rb +1 -1
- data/lib/generators/auxiliary_rails/templates/errors_controller/errors_controller_template.rb +15 -0
- data/lib/generators/auxiliary_rails/templates/errors_controller/not_found_template.html.erb +2 -0
- data/lib/generators/auxiliary_rails/templates/errors_controller/show_template.html.erb +1 -0
- data/lib/generators/auxiliary_rails/templates/errors_controller/unacceptable_template.html.erb +2 -0
- data/lib/generators/auxiliary_rails/templates/services/service_spec_template.rb +5 -0
- data/lib/generators/auxiliary_rails/templates/services/service_template.rb +10 -0
- metadata +24 -111
@@ -1,10 +1,50 @@
|
|
1
1
|
module AuxiliaryRails
|
2
2
|
module Application
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
# @abstract
|
4
|
+
class Error < RuntimeError
|
5
|
+
attr_accessor :context
|
6
|
+
attr_reader :exc, :severity
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def i18n_scope
|
10
|
+
"errors.#{name.underscore}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [self] Wraps exception into a new ApplicationError object
|
14
|
+
def wrap(exc, context: {}, severity: nil)
|
15
|
+
new(exc.message, context: context, exc: exc, severity: severity)
|
16
|
+
end
|
7
17
|
end
|
18
|
+
|
19
|
+
def initialize(message = nil, context: {}, exc: nil, severity: nil)
|
20
|
+
super(message)
|
21
|
+
|
22
|
+
self.context = default_context.merge(context || {})
|
23
|
+
self.exc = exc
|
24
|
+
self.severity = severity&.to_sym || default_severity
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_context
|
28
|
+
{}
|
29
|
+
end
|
30
|
+
|
31
|
+
def default_severity
|
32
|
+
:error
|
33
|
+
end
|
34
|
+
|
35
|
+
def friendly_message
|
36
|
+
I18n.t(:default,
|
37
|
+
scope: self.class.i18n_scope,
|
38
|
+
default: 'We are sorry, but something went wrong.')
|
39
|
+
end
|
40
|
+
|
41
|
+
def report
|
42
|
+
raise NotImplementedError
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
attr_writer :exc, :severity
|
8
48
|
end
|
9
49
|
end
|
10
50
|
end
|
@@ -1,30 +1,34 @@
|
|
1
|
+
require 'auxiliary_rails/concerns/callable'
|
1
2
|
require 'auxiliary_rails/concerns/errorable'
|
2
3
|
require 'dry/core/class_attributes'
|
3
4
|
require 'dry-initializer'
|
4
5
|
|
5
6
|
module AuxiliaryRails
|
6
7
|
module Application
|
8
|
+
# @abstract
|
7
9
|
class Query
|
8
10
|
extend Dry::Core::ClassAttributes
|
9
11
|
extend Dry::Initializer
|
12
|
+
include AuxiliaryRails::Concerns::Callable
|
10
13
|
include AuxiliaryRails::Concerns::Errorable
|
11
14
|
|
12
15
|
defines :default_relation
|
13
16
|
|
14
|
-
option :relation, default: proc {
|
15
|
-
|
16
|
-
def self.call(*args)
|
17
|
-
new(*args).call
|
18
|
-
end
|
17
|
+
option :relation, default: proc {}
|
19
18
|
|
20
19
|
def call
|
21
20
|
ensure_proper_relation_types!
|
22
21
|
|
23
22
|
perform
|
24
23
|
|
24
|
+
if !queriable_object?(query)
|
25
|
+
error!('Invalid class of resulted query')
|
26
|
+
end
|
27
|
+
|
25
28
|
query
|
26
29
|
end
|
27
30
|
|
31
|
+
# @abstract
|
28
32
|
def perform
|
29
33
|
raise NotImplementedError
|
30
34
|
end
|
@@ -41,7 +45,7 @@ module AuxiliaryRails
|
|
41
45
|
|
42
46
|
private
|
43
47
|
|
44
|
-
# rubocop:disable
|
48
|
+
# rubocop:disable Style/GuardClause
|
45
49
|
def ensure_proper_relation_types!
|
46
50
|
if self.class.default_relation.nil?
|
47
51
|
error!('Undefined `default_relation`')
|
@@ -53,7 +57,7 @@ module AuxiliaryRails
|
|
53
57
|
error!('Invalid class of `relation` option')
|
54
58
|
end
|
55
59
|
end
|
56
|
-
# rubocop:enable
|
60
|
+
# rubocop:enable Style/GuardClause
|
57
61
|
|
58
62
|
def queriable_object?(object)
|
59
63
|
object.is_a?(ActiveRecord::Relation)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/ordered_options'
|
3
|
+
|
4
|
+
module AuxiliaryRails
|
5
|
+
module Application
|
6
|
+
module Service
|
7
|
+
def self.included(klass)
|
8
|
+
raise AuxiliaryRails::Error,
|
9
|
+
"Use `extend` insted of `include` for #{self} in #{klass}"
|
10
|
+
end
|
11
|
+
|
12
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
13
|
+
def config
|
14
|
+
return @config if @config.present?
|
15
|
+
|
16
|
+
# load from constant
|
17
|
+
if const_defined?(:CONFIG)
|
18
|
+
return @config = ActiveSupport::OrderedOptions.new.update(const_get(:CONFIG))
|
19
|
+
end
|
20
|
+
|
21
|
+
service_name = name.underscore
|
22
|
+
|
23
|
+
# load from service config file
|
24
|
+
if Rails.root.join("config/services/#{service_name}.yml").exist?
|
25
|
+
return @config = Rails.application.config_for("services/#{service_name}")
|
26
|
+
end
|
27
|
+
|
28
|
+
# load from application config
|
29
|
+
if Object.const_defined?(:Config) && Config.respond_to?(:const_name)
|
30
|
+
app_config = Object.const_get(Config.const_name)
|
31
|
+
if app_config.dig(:services, service_name).present?
|
32
|
+
return @config = app_config[:services][service_name]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
raise AuxiliaryRails::Error,
|
37
|
+
"Unable to find suitable config for #{name} module"
|
38
|
+
end
|
39
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module AuxiliaryRails
|
2
|
+
module Concerns
|
3
|
+
module Callable
|
4
|
+
def self.included(klass)
|
5
|
+
klass.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
# Initializes object and runs <tt>#call</tt> method.
|
10
|
+
#
|
11
|
+
# @return [self]
|
12
|
+
def call(*args, **kws)
|
13
|
+
new(*args, **kws).call
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# @abstract
|
18
|
+
def call
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,22 +1,22 @@
|
|
1
1
|
module AuxiliaryRails
|
2
2
|
module Concerns
|
3
3
|
module Errorable
|
4
|
-
|
4
|
+
extend ActiveSupport::Concern
|
5
5
|
|
6
|
-
|
7
|
-
message ||= "`#{self.class}` raised error."
|
8
|
-
raise error_class, message
|
9
|
-
end
|
10
|
-
|
11
|
-
if defined?(ApplicationError)
|
12
|
-
def error_class
|
13
|
-
ApplicationError
|
14
|
-
end
|
15
|
-
else
|
6
|
+
class_methods do
|
16
7
|
def error_class
|
17
|
-
|
8
|
+
if Object.const_defined?(:ApplicationError)
|
9
|
+
ApplicationError
|
10
|
+
else
|
11
|
+
AuxiliaryRails::Application::Error
|
12
|
+
end
|
18
13
|
end
|
19
14
|
end
|
15
|
+
|
16
|
+
def error!(message = nil)
|
17
|
+
message ||= "`#{self.class}` raised error."
|
18
|
+
raise self.class.error_class, message
|
19
|
+
end
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support/concern'
|
2
|
+
require 'auxiliary_rails/concerns/callable'
|
2
3
|
require 'auxiliary_rails/concerns/errorable'
|
3
4
|
|
4
5
|
module AuxiliaryRails
|
@@ -6,14 +7,9 @@ module AuxiliaryRails
|
|
6
7
|
module Performable
|
7
8
|
extend ActiveSupport::Concern
|
8
9
|
include ActiveModel::Validations
|
10
|
+
include AuxiliaryRails::Concerns::Callable
|
9
11
|
include AuxiliaryRails::Concerns::Errorable
|
10
12
|
|
11
|
-
class_methods do
|
12
|
-
def call(*args)
|
13
|
-
new(*args).call
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
13
|
def call(options = {})
|
18
14
|
ensure_empty_status!
|
19
15
|
|
@@ -58,24 +54,15 @@ module AuxiliaryRails
|
|
58
54
|
# Provides ability to execude block of the code depending on
|
59
55
|
# command execution status
|
60
56
|
#
|
61
|
-
# @param status [
|
62
|
-
# @param &
|
57
|
+
# @param status [Symbol] Desired command status
|
58
|
+
# @param &block Code to be executed if status matches
|
63
59
|
# @return [self]
|
64
|
-
def on(status, &
|
65
|
-
|
66
|
-
|
67
|
-
return self unless status?(status)
|
68
|
-
|
69
|
-
yield(self) if block_given?
|
60
|
+
def on(status, &block)
|
61
|
+
yield(self) if status?(status) && block
|
70
62
|
|
71
63
|
self
|
72
64
|
end
|
73
65
|
|
74
|
-
# Shortcut for <tt>ActiveRecord::Base.transaction</tt>
|
75
|
-
def transaction(&block)
|
76
|
-
ActiveRecord::Base.transaction(&block) if block_given?
|
77
|
-
end
|
78
|
-
|
79
66
|
protected
|
80
67
|
|
81
68
|
attr_accessor :performable_status
|
@@ -86,19 +73,19 @@ module AuxiliaryRails
|
|
86
73
|
def ensure_empty_errors!
|
87
74
|
return if errors.empty?
|
88
75
|
|
89
|
-
error!("
|
76
|
+
error!("'#{self.class}' contains errors.")
|
90
77
|
end
|
91
78
|
|
92
79
|
def ensure_empty_status!
|
93
80
|
return if performable_status.nil?
|
94
81
|
|
95
|
-
error!("
|
82
|
+
error!("'#{self.class}' was already executed.")
|
96
83
|
end
|
97
84
|
|
98
85
|
def ensure_execution!
|
99
86
|
return if performable_status.present?
|
100
87
|
|
101
|
-
error!("
|
88
|
+
error!("'#{self.class}' was not executed yet.")
|
102
89
|
end
|
103
90
|
|
104
91
|
# Sets command's status to <tt>failure</tt>.
|
@@ -123,6 +110,11 @@ module AuxiliaryRails
|
|
123
110
|
self.performable_status = :success
|
124
111
|
self
|
125
112
|
end
|
113
|
+
|
114
|
+
# Shortcut for <tt>ActiveRecord::Base.transaction</tt>
|
115
|
+
def transaction(&block)
|
116
|
+
ActiveRecord::Base.transaction(&block) if block
|
117
|
+
end
|
126
118
|
end
|
127
119
|
end
|
128
120
|
end
|
@@ -1,9 +1,10 @@
|
|
1
|
+
require 'rails/railtie'
|
1
2
|
require 'auxiliary_rails/view_helpers'
|
2
3
|
|
3
4
|
module AuxiliaryRails
|
4
5
|
class Railtie < Rails::Railtie
|
5
6
|
initializer 'auxiliary_rails.view_helpers' do
|
6
|
-
ActionView::Base.
|
7
|
+
ActionView::Base.include ViewHelpers
|
7
8
|
end
|
8
9
|
end
|
9
10
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module AuxiliaryRails
|
2
|
+
module ViewHelpers
|
3
|
+
module DisplayHelper
|
4
|
+
DISPLAY_NAME_METHODS = %i[
|
5
|
+
display_name
|
6
|
+
name
|
7
|
+
title
|
8
|
+
full_name
|
9
|
+
username
|
10
|
+
email
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
def display_name(resource)
|
14
|
+
return if resource.nil?
|
15
|
+
|
16
|
+
DISPLAY_NAME_METHODS.each do |method_name|
|
17
|
+
if resource.respond_to?(method_name)
|
18
|
+
return resource.public_send(method_name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
if resource.respond_to?(:model_name) && resource.respond_to?(:id)
|
23
|
+
return "#{resource.model_name.human} ##{resource.id}"
|
24
|
+
end
|
25
|
+
|
26
|
+
resource.to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/auxiliary_rails.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'auxiliary_rails/application/command'
|
2
|
-
require 'auxiliary_rails/application/form'
|
3
2
|
require 'auxiliary_rails/application/error'
|
3
|
+
require 'auxiliary_rails/application/form'
|
4
|
+
require 'auxiliary_rails/application/service'
|
4
5
|
require 'auxiliary_rails/application/query'
|
6
|
+
|
5
7
|
require 'auxiliary_rails/railtie'
|
6
8
|
require 'auxiliary_rails/version'
|
7
9
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rails'
|
2
|
+
|
3
|
+
module AuxiliaryRails
|
4
|
+
class InstallErrorsControllerGenerator < ::Rails::Generators::Base
|
5
|
+
source_root File.expand_path('templates/errors_controller', __dir__)
|
6
|
+
|
7
|
+
VIEW_TEMPLATES = %w[show not_found unacceptable].freeze
|
8
|
+
|
9
|
+
def copy_controller_file
|
10
|
+
copy_file 'errors_controller_template.rb',
|
11
|
+
'app/controllers/errors_controller.rb'
|
12
|
+
end
|
13
|
+
|
14
|
+
def copy_view_files
|
15
|
+
VIEW_TEMPLATES.each do |tempate_name|
|
16
|
+
copy_file "#{tempate_name}_template.html.erb",
|
17
|
+
"app/views/errors/#{tempate_name}.html.erb"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_route
|
22
|
+
route "match '/404', to: 'errors#not_found', via: :all"
|
23
|
+
route "match '/422', to: 'errors#unacceptable', via: :all"
|
24
|
+
route "match '/500', to: 'errors#show', via: :all"
|
25
|
+
end
|
26
|
+
|
27
|
+
def add_exceptions_app_config
|
28
|
+
application 'config.exceptions_app = routes'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rails'
|
2
|
+
|
3
|
+
module AuxiliaryRails
|
4
|
+
class ServiceGenerator < ::Rails::Generators::NamedBase
|
5
|
+
desc 'Stubs out a new Service and spec.'
|
6
|
+
|
7
|
+
source_root File.expand_path('templates/services', __dir__)
|
8
|
+
|
9
|
+
class_option :path,
|
10
|
+
type: :string,
|
11
|
+
default: 'app/services',
|
12
|
+
desc: 'Service location'
|
13
|
+
|
14
|
+
def create_service_dir
|
15
|
+
FileUtils.mkdir_p("#{service_file_path}/#{service_file_name}")
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_service_file
|
19
|
+
FileUtils.mkdir_p(service_file_path)
|
20
|
+
template 'service_template.rb',
|
21
|
+
"#{service_file_path}/#{service_file_name}.rb"
|
22
|
+
end
|
23
|
+
|
24
|
+
def create_service_spec_file
|
25
|
+
FileUtils.mkdir_p(service_spec_path)
|
26
|
+
template 'service_spec_template.rb',
|
27
|
+
"#{service_spec_path}/#{service_file_name}_spec.rb"
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def service_module_name
|
33
|
+
"#{class_name.gsub(/Service$/, '')}Service"
|
34
|
+
end
|
35
|
+
|
36
|
+
def service_file_name
|
37
|
+
service_module_name.underscore
|
38
|
+
end
|
39
|
+
|
40
|
+
def service_file_path
|
41
|
+
options[:path]
|
42
|
+
end
|
43
|
+
|
44
|
+
def service_spec_path
|
45
|
+
service_file_path.gsub(%r{^app/}, 'spec/')
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class ErrorsController < ApplicationController
|
2
|
+
DEFAULT_ERROR_STATUS = 500
|
3
|
+
|
4
|
+
def show
|
5
|
+
render status: params[:status] || DEFAULT_ERROR_STATUS
|
6
|
+
end
|
7
|
+
|
8
|
+
def not_found
|
9
|
+
render status: :not_found
|
10
|
+
end
|
11
|
+
|
12
|
+
def unacceptable
|
13
|
+
render status: :unprocessable_entity
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<h1>We're sorry, but something went wrong.</h1>
|