auxiliary_rails 0.2.0 → 0.4.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/.gitlab-ci.yml +26 -0
- data/.rubocop.yml +31 -3
- data/.rubocop_todo.yml +3 -19
- data/.ruby-version +1 -1
- data/.yardopts +5 -0
- data/CHANGELOG.md +39 -2
- data/CODE_OF_CONDUCT.md +128 -0
- data/CONTRIBUTING.md +0 -6
- data/Gemfile.lock +164 -132
- data/README.md +267 -5
- data/auxiliary_rails.gemspec +18 -13
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bitbucket-pipelines.yml +35 -0
- data/lib/auxiliary_rails/application/command.rb +53 -0
- data/lib/auxiliary_rails/application/error.rb +50 -0
- data/lib/auxiliary_rails/application/form.rb +31 -0
- data/lib/auxiliary_rails/application/query.rb +75 -0
- data/lib/auxiliary_rails/application/service.rb +42 -0
- data/lib/auxiliary_rails/cli.rb +18 -5
- data/lib/auxiliary_rails/concerns/callable.rb +23 -0
- data/lib/auxiliary_rails/concerns/errorable.rb +22 -0
- data/lib/auxiliary_rails/concerns/performable.rb +120 -0
- 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 +7 -3
- data/lib/generators/auxiliary_rails/command_generator.rb +1 -1
- data/lib/generators/auxiliary_rails/install_commands_generator.rb +5 -0
- data/lib/generators/auxiliary_rails/install_errors_controller_generator.rb +31 -0
- data/lib/generators/auxiliary_rails/install_generator.rb +1 -1
- data/lib/generators/auxiliary_rails/service_generator.rb +48 -0
- data/lib/generators/auxiliary_rails/templates/application_error_template.rb +1 -1
- data/lib/generators/auxiliary_rails/templates/commands/application_command_template.rb +1 -1
- data/lib/generators/auxiliary_rails/templates/commands/command_spec_template.rb +1 -1
- data/lib/generators/auxiliary_rails/templates/commands/command_template.rb +2 -2
- data/lib/generators/auxiliary_rails/templates/commands/commands.en_template.yml +5 -0
- 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
- data/templates/rails/elementary.rb +39 -10
- metadata +87 -32
- data/lib/auxiliary_rails/abstract_command.rb +0 -95
- data/lib/auxiliary_rails/abstract_error.rb +0 -7
- data/lib/generators/auxiliary_rails/install_rubocop_generator.rb +0 -29
- data/lib/generators/auxiliary_rails/templates/rubocop/rubocop_auxiliary_rails_template.yml +0 -65
- data/lib/generators/auxiliary_rails/templates/rubocop/rubocop_template.yml +0 -11
data/auxiliary_rails.gemspec
CHANGED
@@ -8,24 +8,24 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ['Dmitry Babenko', 'ErgoServ']
|
9
9
|
spec.email = ['dmitry@ergoserv.com', 'hello@ergoserv.com']
|
10
10
|
|
11
|
-
spec.summary
|
12
|
-
|
13
|
-
|
11
|
+
spec.summary = <<~DESC
|
12
|
+
AuxiliaryRails provides extra layers and utils
|
13
|
+
for helping to build solid and clean Ruby on Rails applications
|
14
|
+
DESC
|
15
|
+
spec.description = <<~DESC
|
14
16
|
AuxiliaryRails is a collection of classes, configs, scripts,
|
15
17
|
generators for Ruby on Rails helping you get things done, better.
|
16
18
|
DESC
|
17
19
|
spec.homepage = 'https://github.com/ergoserv/auxiliary_rails'
|
18
20
|
spec.license = 'MIT'
|
19
21
|
|
20
|
-
|
21
|
-
spec.metadata['homepage_uri'] = 'https://github.com/ergoserv/auxiliary_rails'
|
22
|
-
spec.metadata['source_code_uri'] = 'https://github.com/ergoserv/auxiliary_rails'
|
23
|
-
spec.metadata['changelog_uri'] = 'https://github.com/ergoserv/auxiliary_rails/blob/master/CHANGELOG.md'
|
24
|
-
else
|
25
|
-
raise 'RubyGems 2.0 or newer is required'
|
26
|
-
end
|
22
|
+
raise 'RubyGems 2.0 or newer is required' unless spec.respond_to?(:metadata)
|
27
23
|
|
28
|
-
spec.
|
24
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
25
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
26
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/releases"
|
27
|
+
|
28
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
29
29
|
`git ls-files -z`.split("\x0").reject do |f|
|
30
30
|
f.match(%r{^(test|spec|features)/})
|
31
31
|
end
|
@@ -33,15 +33,20 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.executables = ['auxiliary_rails']
|
34
34
|
spec.require_paths = ['lib']
|
35
35
|
|
36
|
+
spec.required_ruby_version = '>= 2.5'
|
37
|
+
|
36
38
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
37
39
|
spec.add_development_dependency 'pry'
|
38
|
-
spec.add_development_dependency 'rails', '>= 5.2', '< 7'
|
39
40
|
spec.add_development_dependency 'rake'
|
40
41
|
spec.add_development_dependency 'rspec', '~> 3.8'
|
41
|
-
spec.add_development_dependency 'rubocop'
|
42
|
+
spec.add_development_dependency 'rubocop', '1.20.0'
|
42
43
|
spec.add_development_dependency 'rubocop-performance'
|
44
|
+
spec.add_development_dependency 'rubocop-rake'
|
43
45
|
spec.add_development_dependency 'rubocop-rspec'
|
44
46
|
|
47
|
+
spec.add_runtime_dependency 'dry-core'
|
48
|
+
spec.add_runtime_dependency 'dry-initializer'
|
45
49
|
spec.add_runtime_dependency 'dry-initializer-rails'
|
50
|
+
spec.add_runtime_dependency 'rails', '>= 5.2'
|
46
51
|
spec.add_runtime_dependency 'thor'
|
47
52
|
end
|
data/bin/rspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rspec-core", "rspec")
|
data/bin/rubocop
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rubocop' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
require "pathname"
|
12
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
13
|
+
Pathname.new(__FILE__).realpath)
|
14
|
+
|
15
|
+
bundle_binstub = File.expand_path("../bundle", __FILE__)
|
16
|
+
|
17
|
+
if File.file?(bundle_binstub)
|
18
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
19
|
+
load(bundle_binstub)
|
20
|
+
else
|
21
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
22
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
require "rubygems"
|
27
|
+
require "bundler/setup"
|
28
|
+
|
29
|
+
load Gem.bin_path("rubocop", "rubocop")
|
@@ -0,0 +1,35 @@
|
|
1
|
+
image: ruby:2.6.5
|
2
|
+
|
3
|
+
definitions:
|
4
|
+
caches:
|
5
|
+
bundler: vendor/bundle
|
6
|
+
|
7
|
+
pipelines:
|
8
|
+
default:
|
9
|
+
- step:
|
10
|
+
name: Build
|
11
|
+
caches:
|
12
|
+
- bundler
|
13
|
+
script:
|
14
|
+
- gem install bundler
|
15
|
+
- bundle config set path vendor/bundle
|
16
|
+
- bundle install
|
17
|
+
- parallel:
|
18
|
+
- step:
|
19
|
+
name: RSpec
|
20
|
+
caches:
|
21
|
+
- bundler
|
22
|
+
script:
|
23
|
+
- gem install bundler
|
24
|
+
- bundle config set path vendor/bundle
|
25
|
+
- bundle install
|
26
|
+
- bundle exec rspec
|
27
|
+
- step:
|
28
|
+
name: RuboCop
|
29
|
+
caches:
|
30
|
+
- bundler
|
31
|
+
script:
|
32
|
+
- gem install bundler
|
33
|
+
- bundle config set path vendor/bundle
|
34
|
+
- bundle install
|
35
|
+
- bundle exec rubocop
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
require 'auxiliary_rails/concerns/performable'
|
3
|
+
require 'dry-initializer-rails'
|
4
|
+
|
5
|
+
module AuxiliaryRails
|
6
|
+
module Application
|
7
|
+
# @abstract
|
8
|
+
class Command
|
9
|
+
extend Dry::Initializer
|
10
|
+
include AuxiliaryRails::Concerns::Performable
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# @!method param(name, options = {})
|
14
|
+
# Defines param using <tt>Dry::Initializer</tt> format.
|
15
|
+
#
|
16
|
+
# @see Dry::Initializer
|
17
|
+
# @see https://dry-rb.org/gems/dry-initializer/3.0/params-and-options/
|
18
|
+
#
|
19
|
+
# @param name [Symbol]
|
20
|
+
# @param options [Hash]
|
21
|
+
|
22
|
+
# @!method option(name, options = {})
|
23
|
+
# Defines option using <tt>Dry::Initializer</tt> format.
|
24
|
+
|
25
|
+
# @see Dry::Initializer
|
26
|
+
# @see https://dry-rb.org/gems/dry-initializer/3.0/params-and-options/
|
27
|
+
#
|
28
|
+
# @param name [Symbol]
|
29
|
+
# @param options [Hash]
|
30
|
+
|
31
|
+
# @!method call(*args, **kws)
|
32
|
+
# @see AuxiliaryRails::Corcerns::Callable
|
33
|
+
# @return [self]
|
34
|
+
|
35
|
+
# Defines `scope` for <tt>ActiveModel::Translation</tt>
|
36
|
+
#
|
37
|
+
# @return [Symbol]
|
38
|
+
def i18n_scope
|
39
|
+
:commands
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
# Shortcut reader for attributes defined by <tt>Dry::Initializer</tt>
|
46
|
+
#
|
47
|
+
# @return [Hash]
|
48
|
+
def arguments
|
49
|
+
self.class.dry_initializer.attributes(self)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module AuxiliaryRails
|
2
|
+
module Application
|
3
|
+
# @abstract
|
4
|
+
class Error < RuntimeError
|
5
|
+
attr_accessor :context
|
6
|
+
attr_reader :exception, :severity
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def i18n_scope
|
10
|
+
"errors.#{name.underscore}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [self] Wraps exception into a new Application Error object
|
14
|
+
def wrap(exception, context: {}, severity: nil)
|
15
|
+
new(exception.message, context: context, exception: exception, severity: severity)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(message = nil, context: {}, exception: nil, severity: nil)
|
20
|
+
super message
|
21
|
+
|
22
|
+
self.context = default_context.merge(context || {})
|
23
|
+
self.exception = exception
|
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 :exception, :severity
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
require 'auxiliary_rails/concerns/performable'
|
3
|
+
|
4
|
+
module AuxiliaryRails
|
5
|
+
module Application
|
6
|
+
# @abstract
|
7
|
+
class Form
|
8
|
+
include ActiveModel::Model
|
9
|
+
include ActiveModel::Attributes
|
10
|
+
include ActiveModel::AttributeAssignment
|
11
|
+
include AuxiliaryRails::Concerns::Performable
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# Defines `scope` for <tt>ActiveModel::Translation</tt>
|
15
|
+
#
|
16
|
+
# @return [Symbol]
|
17
|
+
def i18n_scope
|
18
|
+
:forms
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Indicates object persistence.
|
23
|
+
#
|
24
|
+
# In case of form as performable object that means that form
|
25
|
+
# was executed with success.
|
26
|
+
def persisted?
|
27
|
+
performable_status == true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'auxiliary_rails/concerns/callable'
|
2
|
+
require 'auxiliary_rails/concerns/errorable'
|
3
|
+
require 'dry/core/class_attributes'
|
4
|
+
require 'dry-initializer'
|
5
|
+
|
6
|
+
module AuxiliaryRails
|
7
|
+
module Application
|
8
|
+
# @abstract
|
9
|
+
class Query
|
10
|
+
extend Dry::Core::ClassAttributes
|
11
|
+
extend Dry::Initializer
|
12
|
+
include AuxiliaryRails::Concerns::Callable
|
13
|
+
include AuxiliaryRails::Concerns::Errorable
|
14
|
+
|
15
|
+
defines :default_relation
|
16
|
+
|
17
|
+
option :relation, default: proc {}
|
18
|
+
|
19
|
+
def call
|
20
|
+
ensure_proper_relation_types!
|
21
|
+
|
22
|
+
perform
|
23
|
+
|
24
|
+
if !queriable_object?(query)
|
25
|
+
error!('Invalid class of resulted query')
|
26
|
+
end
|
27
|
+
|
28
|
+
query
|
29
|
+
end
|
30
|
+
|
31
|
+
# @abstract
|
32
|
+
def perform
|
33
|
+
raise NotImplementedError
|
34
|
+
end
|
35
|
+
|
36
|
+
def method_missing(method_name, *args, &block)
|
37
|
+
super unless query.respond_to?(method_name)
|
38
|
+
|
39
|
+
query.send(method_name, *args, &block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def respond_to_missing?(method_name, include_private = false)
|
43
|
+
query.respond_to?(method_name) || super
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# rubocop:disable Style/GuardClause
|
49
|
+
def ensure_proper_relation_types!
|
50
|
+
if self.class.default_relation.nil?
|
51
|
+
error!('Undefined `default_relation`')
|
52
|
+
end
|
53
|
+
if !queriable_object?(self.class.default_relation)
|
54
|
+
error!('Invalid class of `default_relation`')
|
55
|
+
end
|
56
|
+
if !relation.nil? && !queriable_object?(relation)
|
57
|
+
error!('Invalid class of `relation` option')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
# rubocop:enable Style/GuardClause
|
61
|
+
|
62
|
+
def queriable_object?(object)
|
63
|
+
object.is_a?(ActiveRecord::Relation)
|
64
|
+
end
|
65
|
+
|
66
|
+
def query(scoped_query = nil)
|
67
|
+
@query ||= (relation || self.class.default_relation)
|
68
|
+
|
69
|
+
@query = scoped_query unless scoped_query.nil?
|
70
|
+
|
71
|
+
@query
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -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
|
data/lib/auxiliary_rails/cli.rb
CHANGED
@@ -6,6 +6,10 @@ module AuxiliaryRails
|
|
6
6
|
|
7
7
|
TEMPLATES_DIR =
|
8
8
|
File.expand_path("#{__dir__}/../../templates/")
|
9
|
+
REPOSITORY_URL =
|
10
|
+
'https://raw.githubusercontent.com/ergoserv/' \
|
11
|
+
'auxiliary_rails/develop/templates'
|
12
|
+
.freeze
|
9
13
|
|
10
14
|
desc 'new APP_PATH', 'Create Rails application from template'
|
11
15
|
long_desc <<-LONGDESC
|
@@ -17,20 +21,29 @@ module AuxiliaryRails
|
|
17
21
|
option :database,
|
18
22
|
default: 'postgresql',
|
19
23
|
type: :string
|
20
|
-
option :
|
24
|
+
option :template,
|
21
25
|
default: 'elementary',
|
22
26
|
type: :string
|
27
|
+
option :develop,
|
28
|
+
default: false,
|
29
|
+
type: :boolean
|
23
30
|
def new(app_path)
|
24
31
|
run "rails new #{app_path} " \
|
25
|
-
'--skip-action-cable --skip-coffee --skip-test ' \
|
26
32
|
"--database=#{options[:database]} " \
|
27
|
-
"--template=#{rails_template_path(options[:
|
33
|
+
"--template=#{rails_template_path(options[:template])} " \
|
34
|
+
'--skip-action-cable ' \
|
35
|
+
'--skip-coffee ' \
|
36
|
+
'--skip-test '
|
28
37
|
end
|
29
38
|
|
30
39
|
private
|
31
40
|
|
32
|
-
def rails_template_path(
|
33
|
-
|
41
|
+
def rails_template_path(template)
|
42
|
+
if options[:develop] == true
|
43
|
+
"#{REPOSITORY_URL}/rails/#{template}.rb"
|
44
|
+
else
|
45
|
+
"#{TEMPLATES_DIR}/rails/#{template}.rb"
|
46
|
+
end
|
34
47
|
end
|
35
48
|
end
|
36
49
|
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
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module AuxiliaryRails
|
2
|
+
module Concerns
|
3
|
+
module Errorable
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
class_methods do
|
7
|
+
def error_class
|
8
|
+
if Object.const_defined?(:ApplicationError)
|
9
|
+
ApplicationError
|
10
|
+
else
|
11
|
+
AuxiliaryRails::Application::Error
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def error!(message = nil)
|
17
|
+
message ||= "`#{self.class}` raised error."
|
18
|
+
raise self.class.error_class, message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'auxiliary_rails/concerns/callable'
|
3
|
+
require 'auxiliary_rails/concerns/errorable'
|
4
|
+
|
5
|
+
module AuxiliaryRails
|
6
|
+
module Concerns
|
7
|
+
module Performable
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
include ActiveModel::Validations
|
10
|
+
include AuxiliaryRails::Concerns::Callable
|
11
|
+
include AuxiliaryRails::Concerns::Errorable
|
12
|
+
|
13
|
+
def call(options = {})
|
14
|
+
ensure_empty_status!
|
15
|
+
|
16
|
+
if options[:validate!] == true
|
17
|
+
validate!
|
18
|
+
elsif options[:validate] != false && invalid?
|
19
|
+
return failure!
|
20
|
+
end
|
21
|
+
|
22
|
+
perform
|
23
|
+
|
24
|
+
ensure_execution!
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
# Describes business logic.
|
29
|
+
#
|
30
|
+
# Method <b>should always</b> return <tt>success!</tt>
|
31
|
+
# or <tt>failure!</tt> methods in order pro provide further
|
32
|
+
# correct method chaining.
|
33
|
+
#
|
34
|
+
# @abstract
|
35
|
+
# @return [self]
|
36
|
+
def perform
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
def failure?
|
41
|
+
status?(:failure)
|
42
|
+
end
|
43
|
+
|
44
|
+
def status?(value)
|
45
|
+
ensure_execution!
|
46
|
+
|
47
|
+
performable_status == value&.to_sym
|
48
|
+
end
|
49
|
+
|
50
|
+
def success?
|
51
|
+
status?(:success)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Provides ability to execude block of the code depending on
|
55
|
+
# command execution status
|
56
|
+
#
|
57
|
+
# @param status [Symbol] Desired command status
|
58
|
+
# @param &block Code to be executed if status matches
|
59
|
+
# @return [self]
|
60
|
+
def on(status, &block)
|
61
|
+
yield(self) if status?(status) && block
|
62
|
+
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
attr_accessor :performable_status
|
69
|
+
|
70
|
+
# @raise [AuxiliaryRails::Application::Error]
|
71
|
+
# Error happens if command contains any errors.
|
72
|
+
# @return [nil]
|
73
|
+
def ensure_empty_errors!
|
74
|
+
return if errors.empty?
|
75
|
+
|
76
|
+
error!("`#{self.class}` contains errors.")
|
77
|
+
end
|
78
|
+
|
79
|
+
def ensure_empty_status!
|
80
|
+
return if performable_status.nil?
|
81
|
+
|
82
|
+
error!("`#{self.class}` was already executed.")
|
83
|
+
end
|
84
|
+
|
85
|
+
def ensure_execution!
|
86
|
+
return if performable_status.present?
|
87
|
+
|
88
|
+
error!("`#{self.class}` was not executed yet.")
|
89
|
+
end
|
90
|
+
|
91
|
+
# Sets command's status to <tt>failure</tt>.
|
92
|
+
#
|
93
|
+
# @return [self]
|
94
|
+
def failure!(message = nil, options = {})
|
95
|
+
ensure_empty_status!
|
96
|
+
|
97
|
+
errors.add(:base, message, options) if message.present?
|
98
|
+
|
99
|
+
self.performable_status = :failure
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
# Sets command's status to <tt>success</tt>.
|
104
|
+
#
|
105
|
+
# @return [self]
|
106
|
+
def success!
|
107
|
+
ensure_empty_errors!
|
108
|
+
ensure_empty_status!
|
109
|
+
|
110
|
+
self.performable_status = :success
|
111
|
+
self
|
112
|
+
end
|
113
|
+
|
114
|
+
# Shortcut for <tt>ActiveRecord::Base.transaction</tt>
|
115
|
+
def transaction(&block)
|
116
|
+
ActiveRecord::Base.transaction(&block) if block
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
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
|