modulorails 0.4.0 → 1.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/.rubocop.yml +45 -0
- data/CHANGELOG.md +10 -0
- data/app/assets/stylesheets/modulorails.css +21 -0
- data/app/helpers/modulorails/application_helper.rb +8 -0
- data/config/locales/en.yml +3 -0
- data/docker-compose.debug.yml +47 -0
- data/lib/generators/modulorails/rubocop/rubocop_generator.rb +24 -0
- data/lib/generators/modulorails/rubocop/templates/rubocop.yml.tt +18 -0
- data/lib/generators/modulorails/service/service_generator.rb +13 -0
- data/lib/generators/modulorails/service/templates/service.rb.tt +28 -0
- data/lib/modulorails/error_data.rb +21 -0
- data/lib/modulorails/errors/base_error.rb +4 -0
- data/lib/modulorails/errors/errors.rb +3 -0
- data/lib/modulorails/errors/invalid_format_error.rb +14 -0
- data/lib/modulorails/errors/invalid_value_error.rb +14 -0
- data/lib/modulorails/railtie.rb +17 -0
- data/lib/modulorails/services/base_service.rb +203 -0
- data/lib/modulorails/services/logs_for_method_service.rb +42 -0
- data/lib/modulorails/services/services.rb +2 -0
- data/lib/modulorails/success_data.rb +17 -0
- data/lib/modulorails/validators/database_configuration.rb +8 -2
- data/lib/modulorails/version.rb +4 -1
- data/lib/modulorails.rb +12 -0
- data/modulorails.gemspec +3 -1
- metadata +47 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 945b70ac74dba5edd9a3e6df1aae0f0244d4a4704487a070c82d0b46580a2285
|
4
|
+
data.tar.gz: 850fcbbf27e12f2b777f0c72deb15a249d84a46fa9dc993fca359dbe79481efe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43ef84405a627ebc3460428add66c7769b626a476c15b4e1e21234dc8ec3e4a03bade4d42bfea506965d1ee9192cfa12aa6b8271989005d0075c292faa951b25
|
7
|
+
data.tar.gz: b76c6a8fd2b32a745cac58298a751c4f120e7165ac3b5c13221dd11602475f4e3bf5aaa398106ef4f6a10e1c93c98e917373aea3c041114cf480820e91cae342
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# The behavior of RuboCop can be controlled via the .rubocop.yml
|
2
|
+
# configuration file. It makes it possible to enable/disable
|
3
|
+
# certain cops (checks) and to alter their behavior if they accept
|
4
|
+
# any parameters. The file can be placed either in your home
|
5
|
+
# directory or in some project directory.
|
6
|
+
#
|
7
|
+
# RuboCop will start looking for the configuration file in the directory
|
8
|
+
# where the inspected file is and continue its way up to the root directory.
|
9
|
+
#
|
10
|
+
# See https://docs.rubocop.org/rubocop/configuration
|
11
|
+
|
12
|
+
# Enabling Rails-specific cops.
|
13
|
+
require: rubocop-rails
|
14
|
+
|
15
|
+
AllCops:
|
16
|
+
# Disable all cops by default to ease the migration. This will probably be removed one day.
|
17
|
+
DisabledByDefault: true
|
18
|
+
# No suggestions since the gem is the sole truth for rubocop configuration.
|
19
|
+
SuggestExtensions: false
|
20
|
+
|
21
|
+
# Excluding most directories with generated files and directories with configuration files.
|
22
|
+
Exclude:
|
23
|
+
- 'vendor/**/*'
|
24
|
+
- 'db/**/*'
|
25
|
+
- 'tmp/**/*'
|
26
|
+
- 'bin/**/*'
|
27
|
+
- 'builds/**/*'
|
28
|
+
- 'Gemfile'
|
29
|
+
- 'config/environments/*'
|
30
|
+
- 'config/puma.rb'
|
31
|
+
- 'config/spring.rb'
|
32
|
+
- 'test/application_system_test_case.rb'
|
33
|
+
- 'test/test_helper.rb'
|
34
|
+
- 'config/initializers/*.rb'
|
35
|
+
- 'spec/spec_helper.rb'
|
36
|
+
- 'node_modules/**/*'
|
37
|
+
- 'spec/**/*'
|
38
|
+
|
39
|
+
# Instructing rubocop about all standard Modulotech environments.
|
40
|
+
Rails/UnknownEnv:
|
41
|
+
Environments:
|
42
|
+
- production
|
43
|
+
- development
|
44
|
+
- test
|
45
|
+
- staging
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,16 @@
|
|
2
2
|
|
3
3
|
This file is used to list changes made in each version of the gem.
|
4
4
|
|
5
|
+
# 1.0.0
|
6
|
+
|
7
|
+
The Rubocop release.
|
8
|
+
|
9
|
+
- Add Modulorails helper `powered_by`.
|
10
|
+
- Add `Modulorails::BaseService`, `Modulorails::LogsForMethodService`,
|
11
|
+
`Modulorails::SuccessData` and `Modulorails::ErrorData`.
|
12
|
+
- Add Rubocop dependency with empty configuration.
|
13
|
+
- Ensure the compatibility of the gem with Ruby 3.0 and Ruby 3.1.
|
14
|
+
|
5
15
|
# 0.4.0
|
6
16
|
|
7
17
|
Fixes, updates and health_check release.
|
@@ -0,0 +1,21 @@
|
|
1
|
+
/**
|
2
|
+
** Modulorails style
|
3
|
+
**/
|
4
|
+
.modulolink {
|
5
|
+
text-decoration: none;
|
6
|
+
font-weight: 500;
|
7
|
+
color: #202D4C;
|
8
|
+
}
|
9
|
+
|
10
|
+
.modulolink span {
|
11
|
+
color: #FFCE19;
|
12
|
+
}
|
13
|
+
|
14
|
+
.modulolink:hover {
|
15
|
+
color: #374D82;
|
16
|
+
text-decoration: none;
|
17
|
+
}
|
18
|
+
|
19
|
+
.modulolink:hover span {
|
20
|
+
color: #FFCB0C;
|
21
|
+
}
|
data/config/locales/en.yml
CHANGED
@@ -14,3 +14,6 @@ en:
|
|
14
14
|
configurable_password: Password is not configurable for test environment
|
15
15
|
configurable_database: Database name is not configurable for test environment
|
16
16
|
configurable_port: Port is not configurable for test environment
|
17
|
+
errors:
|
18
|
+
invalid_format: Invalid format for field %{field}
|
19
|
+
invalid_value: Invalid value for field %{field}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
version: '3.7'
|
2
|
+
|
3
|
+
services:
|
4
|
+
ruby25:
|
5
|
+
image: modulotechgroup/modulorails:testruby25
|
6
|
+
volumes:
|
7
|
+
- .:/app
|
8
|
+
build:
|
9
|
+
context: .
|
10
|
+
dockerfile: Dockerfile.ruby25
|
11
|
+
command: ./entrypoints/appraisal_test.sh
|
12
|
+
|
13
|
+
ruby26:
|
14
|
+
image: modulotechgroup/modulorails:testruby26
|
15
|
+
volumes:
|
16
|
+
- .:/app
|
17
|
+
build:
|
18
|
+
context: .
|
19
|
+
dockerfile: Dockerfile.ruby26
|
20
|
+
command: ./entrypoints/appraisal_test.sh
|
21
|
+
|
22
|
+
ruby27:
|
23
|
+
image: modulotechgroup/modulorails:testruby27
|
24
|
+
volumes:
|
25
|
+
- .:/app
|
26
|
+
build:
|
27
|
+
context: .
|
28
|
+
dockerfile: Dockerfile.ruby27
|
29
|
+
command: ./entrypoints/appraisal_test.sh
|
30
|
+
|
31
|
+
ruby30:
|
32
|
+
image: modulotechgroup/modulorails:testruby30
|
33
|
+
volumes:
|
34
|
+
- .:/app
|
35
|
+
build:
|
36
|
+
context: .
|
37
|
+
dockerfile: Dockerfile.ruby30
|
38
|
+
command: ./entrypoints/appraisal_test.sh
|
39
|
+
|
40
|
+
ruby31:
|
41
|
+
image: modulotechgroup/modulorails:testruby31
|
42
|
+
volumes:
|
43
|
+
- .:/app
|
44
|
+
build:
|
45
|
+
context: .
|
46
|
+
dockerfile: Dockerfile.ruby31
|
47
|
+
command: ./entrypoints/appraisal_test.sh
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
|
5
|
+
class Modulorails::RubocopGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
7
|
+
desc 'This generator creates a configuration for Rubocop'
|
8
|
+
|
9
|
+
def create_config_files
|
10
|
+
rubocop_config_path = Rails.root.join('.rubocop.yml')
|
11
|
+
gitlab_config_path = Rails.root.join('.gitlab-ci.yml')
|
12
|
+
|
13
|
+
template "rubocop.yml", rubocop_config_path, force: true
|
14
|
+
|
15
|
+
unless File.read(gitlab_config_path).match?(/\s+extends:\s+.lint\s*$/)
|
16
|
+
append_file gitlab_config_path do
|
17
|
+
<<~YAML
|
18
|
+
rubocop:
|
19
|
+
extends: .lint
|
20
|
+
YAML
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# The behavior of RuboCop can be controlled via the .rubocop.yml
|
2
|
+
# configuration file. It makes it possible to enable/disable
|
3
|
+
# certain cops (checks) and to alter their behavior if they accept
|
4
|
+
# any parameters. The file can be placed either in your home
|
5
|
+
# directory or in some project directory.
|
6
|
+
#
|
7
|
+
# RuboCop will start looking for the configuration file in the directory
|
8
|
+
# where the inspected file is and continue its way up to the root directory.
|
9
|
+
#
|
10
|
+
# See https://docs.rubocop.org/rubocop/configuration
|
11
|
+
|
12
|
+
inherit_gem:
|
13
|
+
modulorails: .rubocop.yml
|
14
|
+
|
15
|
+
# Take into account the exclude list from the gem
|
16
|
+
inherit_mode:
|
17
|
+
merge:
|
18
|
+
- Exclude
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/generators'
|
4
|
+
|
5
|
+
class Modulorails::ServiceGenerator < Rails::Generators::NamedBase
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
7
|
+
desc 'This generator creates a service inheriting Modulorails::BaseService'
|
8
|
+
argument :arguments, type: :array, default: [], banner: 'argument argument'
|
9
|
+
|
10
|
+
def create_service_files
|
11
|
+
template "service.rb", File.join("app/services", class_path, "#{file_name}_service.rb")
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<% module_namespacing do -%>
|
2
|
+
# @author <INSERT YOUR NAME HERE>
|
3
|
+
# <DESCRIBE YOUR CLASS HERE>
|
4
|
+
<%- if arguments.size >= 1 -%>
|
5
|
+
#
|
6
|
+
<%- arguments.each do |argument| -%>
|
7
|
+
# @!attribute <%= argument %>
|
8
|
+
# @return <DESCRIBE YOUR ARGUMENT HERE>
|
9
|
+
<%- end -%>
|
10
|
+
<%- end -%>
|
11
|
+
class <%= class_name %>Service < ::ApplicationService
|
12
|
+
<%- if arguments.size >= 1 -%>
|
13
|
+
|
14
|
+
def initialize(<%= arguments.join(', ') %>)
|
15
|
+
super()
|
16
|
+
|
17
|
+
<%- arguments.each do |argument| -%>
|
18
|
+
<%= "@#{argument} = #{argument}" %>
|
19
|
+
<%- end -%>
|
20
|
+
end
|
21
|
+
<%- end -%>
|
22
|
+
|
23
|
+
def call
|
24
|
+
# TODO
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
<% end -%>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# @author Matthieu Ciappara <ciappa_m@modulotech>
|
2
|
+
# An error encountered during an operation with additional data.
|
3
|
+
class Modulorails::ErrorData
|
4
|
+
# @!attribute r errors
|
5
|
+
# An error message or an array of error messages (those will be joined by a coma and a space).
|
6
|
+
# @!attribute r exception
|
7
|
+
# The exception that caused the error. Defaults to nil.
|
8
|
+
attr_reader :errors, :exception
|
9
|
+
|
10
|
+
# @param errors [String,Array<String>] An error message or an array of error messages
|
11
|
+
# @param exception [Exception,nil] The exception that caused the error.
|
12
|
+
def initialize(errors, exception: nil)
|
13
|
+
@errors = errors.respond_to?(:join) ? errors.join(', ') : errors.to_s
|
14
|
+
@exception = exception
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [false] An error always means the operation was not a success.
|
18
|
+
def success?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# @author Matthieu CIAPPARA <ciappa_m@modulotech.fr>
|
2
|
+
# An exception representing an invalid format for a given field.
|
3
|
+
class Modulorails::InvalidFormatError < Modulorails::BaseError
|
4
|
+
# @!attribute r field
|
5
|
+
# The name of the field that had a wrong format.
|
6
|
+
attr_reader :field
|
7
|
+
|
8
|
+
# @param field [String]
|
9
|
+
def initialize(field)
|
10
|
+
super(I18n.t('modulorails.errors.invalid_format', field: field))
|
11
|
+
|
12
|
+
@field = field
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# @author Matthieu CIAPPARA <ciappa_m@modulotech.fr>
|
2
|
+
# An exception representing an invalid value for a given field.
|
3
|
+
class Modulorails::InvalidValueError < Modulorails::BaseError
|
4
|
+
# @!attribute r field
|
5
|
+
# The name of the field that had a wrong value.
|
6
|
+
attr_reader :field
|
7
|
+
|
8
|
+
# @param field [String]
|
9
|
+
def initialize(field)
|
10
|
+
super(I18n.t('modulorails.errors.invalid_value', field: field))
|
11
|
+
|
12
|
+
@field = field
|
13
|
+
end
|
14
|
+
end
|
data/lib/modulorails/railtie.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative '../../app/helpers/modulorails/application_helper'
|
2
|
+
|
1
3
|
module Modulorails
|
2
4
|
# Bind in the Rails lifecycle
|
3
5
|
class Railtie < ::Rails::Railtie
|
@@ -15,6 +17,18 @@ module Modulorails
|
|
15
17
|
require 'health_check'
|
16
18
|
end
|
17
19
|
|
20
|
+
initializer 'modulorails.action_view' do
|
21
|
+
ActiveSupport.on_load :action_view do
|
22
|
+
include Modulorails::ApplicationHelper
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
initializer 'modulorails.assets' do |app|
|
27
|
+
%w[stylesheets javascripts].each do |subdirectory|
|
28
|
+
app.config.assets.paths << File.expand_path("../../../app/assets/#{subdirectory}", __FILE__)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
18
32
|
# Sending data after the initialization ensures we can access
|
19
33
|
# all gems, constants and configurations we might need.
|
20
34
|
config.after_initialize do
|
@@ -32,6 +46,9 @@ module Modulorails
|
|
32
46
|
# Check database configuration
|
33
47
|
Modulorails.check_database_config
|
34
48
|
|
49
|
+
# Add/update Rubocop config
|
50
|
+
Modulorails.generate_rubocop_template
|
51
|
+
|
35
52
|
# Gem's self-update if a new version was released
|
36
53
|
Modulorails.self_update
|
37
54
|
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# @author Matthieu CIAPPARA <ciappa_m@modulotech.fr>
|
2
|
+
# The base class for services. Should be implemented by ApplicationService following the model of
|
3
|
+
# ActiveRecord::Base and ApplicationRecord.
|
4
|
+
class Modulorails::BaseService
|
5
|
+
# Allow to instantiate the service and call the service in one go.
|
6
|
+
if Modulorails::COMPARABLE_RUBY_VERSION < Gem::Version.new('3.0')
|
7
|
+
def self.call(*args, &block)
|
8
|
+
new(*args, &block).call
|
9
|
+
end
|
10
|
+
else
|
11
|
+
def self.call(*args, **kwargs, &block)
|
12
|
+
new(*args, **kwargs, &block).call
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# @abstract The main method to implement for your service to do something
|
17
|
+
def call
|
18
|
+
raise NotImplementedError.new('Implement method call on sub-class')
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
self.class.to_s
|
23
|
+
end
|
24
|
+
|
25
|
+
# The method used by Modulorails::LogsForMethodService to log the service name
|
26
|
+
def to_tag
|
27
|
+
self.class.to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
# Shamelessly copied from text_helper
|
31
|
+
def self.pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
|
32
|
+
word = if count == 1 || count =~ /^1(\.0+)?$/
|
33
|
+
singular
|
34
|
+
else
|
35
|
+
plural || singular.pluralize(locale)
|
36
|
+
end
|
37
|
+
|
38
|
+
"#{count || 0} #{word}"
|
39
|
+
end
|
40
|
+
|
41
|
+
protected
|
42
|
+
|
43
|
+
# Wrapper to Modulorails::LogsForMethodService
|
44
|
+
# @param method [#to_s] The method calling `#log`
|
45
|
+
# @param message [Hash,#to_s] The message to log; Hash will be logged after a #to_json call
|
46
|
+
def log(method, message)
|
47
|
+
Modulorails::LogsForMethodService.call(method: method, message: message, tags: [self])
|
48
|
+
end
|
49
|
+
|
50
|
+
# @yield Wrap the given block in an ActiveRecord transaction.
|
51
|
+
# @yieldreturn [Object] Will be available as data of the `SuccessData` returned by the method
|
52
|
+
# @return [SuccessData] If the transaction was not rollbacked; give access to the block's return.
|
53
|
+
# @return [ErrorData] If the transaction was rollbacked; give access to the rollbacking exception.
|
54
|
+
def with_transaction
|
55
|
+
data = nil
|
56
|
+
|
57
|
+
ActiveRecord::Base.transaction do
|
58
|
+
data = yield
|
59
|
+
end
|
60
|
+
|
61
|
+
SuccessData.new(data)
|
62
|
+
rescue ActiveRecord::RecordInvalid => e
|
63
|
+
# Known error, no need for a log, it just needs to be returned
|
64
|
+
ErrorData.new(e.message, exception: e)
|
65
|
+
rescue StandardError => e
|
66
|
+
# Unknown error, log the error
|
67
|
+
Rails.logger.error("#{self}: #{e.message}")
|
68
|
+
Rails.logger.error("Local variables: #{local_variables.map! { |v| { v => eval(v.to_s, binding) } }}")
|
69
|
+
Rails.logger.error(e.backtrace&.join("\n"))
|
70
|
+
|
71
|
+
# Return the error
|
72
|
+
ErrorData.new(e.message, exception: e)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Cast the date/datetime parameters to time with zones.
|
76
|
+
# @param from [String,ActiveSupport::TimeWithZone] the minimum date
|
77
|
+
# @param to [String,ActiveSupport::TimeWithZone] the maximum date
|
78
|
+
# @return [[ActiveSupport::TimeWithZone, ActiveSupport::TimeWithZone]] The given dates casted.
|
79
|
+
def params_to_time(from, to = nil)
|
80
|
+
from = from.is_a?(String) && from.present? ? from.to_time_with_zone : from
|
81
|
+
to = if to.is_a?(String) && to.present?
|
82
|
+
to = to.to_time_with_zone
|
83
|
+
|
84
|
+
# If the right bound is exactly the same as the left one, we add 1 day to the right
|
85
|
+
# one by default.
|
86
|
+
to.present? && to == from ? to + 1.day : to
|
87
|
+
else
|
88
|
+
to
|
89
|
+
end
|
90
|
+
|
91
|
+
[from, to]
|
92
|
+
end
|
93
|
+
|
94
|
+
# Shamelessly copied from text_helper
|
95
|
+
def pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
|
96
|
+
self.class.pluralize(count, singular, plural_arg, plural: plural, locale: locale)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Take a series of keys, dig them into the given params and ensure the found value is a date.
|
100
|
+
# @param keys [Array<Symbol>] The keys to find in the parameters.
|
101
|
+
# @param params [#dig] The parameters to dig in.
|
102
|
+
# @return [ActiveSupport::TimeWithZone] If it is found, the value casted as a datetime.
|
103
|
+
# @return [nil] If the parameter was not found.
|
104
|
+
def parse_date_field(*keys, params:)
|
105
|
+
value = params.dig(*keys)
|
106
|
+
|
107
|
+
return nil unless value
|
108
|
+
|
109
|
+
begin
|
110
|
+
Time.zone.parse(value)
|
111
|
+
rescue ArgumentError
|
112
|
+
raise InvalidFormatError.new(keys.join('/'))
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Take a series of keys, dig them into the given params and ensure the found value is a date.
|
117
|
+
# @param keys [Array<Symbol>] The keys to find in the parameters.
|
118
|
+
# @param params [#dig] The parameters to dig in.
|
119
|
+
# @return [ActiveSupport::TimeWithZone] If it is found, the value casted as a datetime.
|
120
|
+
# @return [nil] If the parameter was not found.
|
121
|
+
def parse_iso8601_field(*keys, params: {})
|
122
|
+
value = params.dig(*keys)
|
123
|
+
|
124
|
+
return nil unless value
|
125
|
+
|
126
|
+
begin
|
127
|
+
Time.zone.iso8601(value)
|
128
|
+
rescue ArgumentError
|
129
|
+
raise InvalidFormatError.new(keys.join('/'))
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Take a series of keys, dig them into the given params and ensure the found value is valid.
|
134
|
+
# @param allowed_values [#include?] Allowed values
|
135
|
+
# @param keys [Array<Symbol>] The keys to find in the parameters.
|
136
|
+
# @param params [#dig] The parameters to dig in.
|
137
|
+
# @param allow_nil [Boolean] Do not raise if value is nil.
|
138
|
+
# @return [ActiveSupport::TimeWithZone] If it is found, the value casted as a datetime.
|
139
|
+
# @return [nil] If the parameter was not found.
|
140
|
+
def parse_enumerated_field(allowed_values, keys, params: {}, allow_nil: true)
|
141
|
+
value = params.dig(*keys)
|
142
|
+
|
143
|
+
if value.respond_to?(:each)
|
144
|
+
raise InvalidValueError.new(keys.join('/')) unless value.all? { |v| allowed_values.include?(v) }
|
145
|
+
else
|
146
|
+
return nil if !value && allow_nil
|
147
|
+
|
148
|
+
raise InvalidValueError.new(keys.join('/')) unless allowed_values.include?(value)
|
149
|
+
end
|
150
|
+
|
151
|
+
value
|
152
|
+
end
|
153
|
+
|
154
|
+
# @param model [#find_by] The model to search in
|
155
|
+
# @param field [Symbol] The field to filter on
|
156
|
+
# @param keys [Array<Symbol>] The keys to search in the params
|
157
|
+
# @param params [#dig] The params to search in
|
158
|
+
# @param allow_nil [Boolean] Raise if the keys are not found; default true
|
159
|
+
# @return [#id, nil] The record corresponding to given field and values (through keys)
|
160
|
+
# @raise [InvalidValueError] When there is no record for given field and values
|
161
|
+
def parse_referential_value(model, field, *keys, params: {}, allow_nil: true)
|
162
|
+
value = params.dig(*keys)
|
163
|
+
|
164
|
+
unless value
|
165
|
+
if allow_nil
|
166
|
+
return nil
|
167
|
+
else
|
168
|
+
raise InvalidValueError.new(keys.join('/'))
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
result = model.find_by(field => value)
|
173
|
+
|
174
|
+
raise InvalidValueError.new(keys.join('/')) if result.nil?
|
175
|
+
|
176
|
+
result
|
177
|
+
end
|
178
|
+
|
179
|
+
# @param model [#where] The model to search in
|
180
|
+
# @param field [Symbol] The field to filter on
|
181
|
+
# @param keys [Array<Symbol>] The keys to search in the params
|
182
|
+
# @param params [#dig] The parmas to search in
|
183
|
+
# @param allow_nil [Boolean] Raise if the keys are not found; default true
|
184
|
+
# @return [#ids, nil] The record corresponding to given field and values (through keys)
|
185
|
+
# @raise [InvalidValueError] When there is no record for given field and values
|
186
|
+
def parse_referential_values(model, field, *keys, params: {}, allow_nil: true)
|
187
|
+
values = params.dig(*keys)
|
188
|
+
|
189
|
+
if values.blank?
|
190
|
+
if allow_nil
|
191
|
+
return nil
|
192
|
+
else
|
193
|
+
raise InvalidValueError.new(keys.join('/'))
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
results = model.where(field => values)
|
198
|
+
|
199
|
+
raise InvalidValueError.new(keys.join('/')) if results.blank?
|
200
|
+
|
201
|
+
results
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# @author Matthieu CIAPPARA <ciappa_m@modulotech.fr>
|
2
|
+
# A service to write formatted debug logs from a method.
|
3
|
+
class Modulorails::LogsForMethodService < Modulorails::BaseService
|
4
|
+
|
5
|
+
# @param method [String] The name of the calling method
|
6
|
+
# @param message [String,#to_json] The body of the log.
|
7
|
+
# @param tags [Array<String,#to_tag>] A list of tags to prefix the log.
|
8
|
+
def initialize(method:, message:, tags: [])
|
9
|
+
super()
|
10
|
+
@method = method
|
11
|
+
@message = message
|
12
|
+
@tags = tags
|
13
|
+
end
|
14
|
+
|
15
|
+
# Write a formatted debug log using given initialization parameters
|
16
|
+
def call
|
17
|
+
# Map the tags (either objects responding to #to_tag or strings) to prefix the log body
|
18
|
+
tag_strings = @tags.map do |tag|
|
19
|
+
tag.respond_to?(:to_tag) ? "[#{tag.to_tag}]" : "[#{tag}]"
|
20
|
+
end
|
21
|
+
|
22
|
+
# If the message respond_to #to_json (and is not a String), use it.
|
23
|
+
@message = jsonify if !@message.is_a?(String) && @message.respond_to?(:to_json)
|
24
|
+
|
25
|
+
# Join the tags
|
26
|
+
tag_string = tag_strings.join
|
27
|
+
|
28
|
+
# Split on newlines to avoid a log of a thousand columns and for each line, prefix the tags
|
29
|
+
# and log it as debug.
|
30
|
+
@message.split("\n").each do |line|
|
31
|
+
msg = "#{tag_string}[#{@method}] #{line}"
|
32
|
+
|
33
|
+
Rails.logger.debug(msg)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def jsonify
|
40
|
+
@message.to_json
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# @author Matthieu Ciappara <ciappa_m@modulotech>
|
2
|
+
# A success resulting from an operation with optional additional data.
|
3
|
+
class Modulorails::SuccessData
|
4
|
+
# @!attribute r data
|
5
|
+
# An object to transport some data (for instance the result of the operation). Defaults to nil.
|
6
|
+
attr_reader :data
|
7
|
+
|
8
|
+
# @param data [Object] An object to transport some data (for instance the result of the operation)
|
9
|
+
def initialize(data=nil)
|
10
|
+
@data = data
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [true] A success always means the operation was a success. ;)
|
14
|
+
def success?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
@@ -52,13 +52,19 @@ module Modulorails
|
|
52
52
|
|
53
53
|
def check_standard_config_file_location
|
54
54
|
# Load the configuration
|
55
|
-
config =
|
55
|
+
config = if Modulorails::COMPARABLE_RUBY_VERSION >= Gem::Version.new('3.1')
|
56
|
+
# Ruby 3.1 uses Psych4 which changes the default way of handling aliases in
|
57
|
+
# `load_file`.
|
58
|
+
Psych.load_file(Rails.root.join('config/database.yml'), aliases: true)
|
59
|
+
else
|
60
|
+
Psych.load_file(Rails.root.join('config/database.yml'))
|
61
|
+
end
|
56
62
|
|
57
63
|
# If no exception was raised, then the database configuration file is at standard location
|
58
64
|
@rules[:standard_config_file_location] = true
|
59
65
|
|
60
66
|
config
|
61
|
-
rescue StandardError =>e
|
67
|
+
rescue StandardError => e
|
62
68
|
# An exception was raised, either the file is not a the standard location, either it just
|
63
69
|
# cannot be read. Either way, we consider the config as invalid
|
64
70
|
@rules[:standard_config_file_location] = false
|
data/lib/modulorails/version.rb
CHANGED
data/lib/modulorails.rb
CHANGED
@@ -6,7 +6,12 @@ require 'modulorails/railtie' if defined?(Rails::Railtie)
|
|
6
6
|
require 'generators/modulorails/gitlabci/gitlabci_generator'
|
7
7
|
require 'generators/modulorails/healthcheck/health_check_generator'
|
8
8
|
require 'generators/modulorails/self_update/self_update_generator'
|
9
|
+
require 'generators/modulorails/rubocop/rubocop_generator'
|
9
10
|
require 'httparty'
|
11
|
+
require 'modulorails/error_data'
|
12
|
+
require 'modulorails/success_data'
|
13
|
+
require 'modulorails/errors/errors'
|
14
|
+
require 'modulorails/services/services'
|
10
15
|
|
11
16
|
# Author: Matthieu 'ciappa_m' Ciappara
|
12
17
|
# The entry point of the gem. It exposes the configurator, the gathered data and the method to
|
@@ -147,5 +152,12 @@ module Modulorails
|
|
147
152
|
|
148
153
|
Modulorails::HealthCheckGenerator.new([], {}, {}).invoke_all
|
149
154
|
end
|
155
|
+
|
156
|
+
# @author Matthieu 'ciappa_m' Ciappara
|
157
|
+
#
|
158
|
+
# Generate a rubocop configuration.
|
159
|
+
def generate_rubocop_template
|
160
|
+
Modulorails::RubocopGenerator.new([], {}, {}).invoke_all
|
161
|
+
end
|
150
162
|
end
|
151
163
|
end
|
data/modulorails.gemspec
CHANGED
@@ -34,7 +34,9 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_runtime_dependency 'httparty'
|
35
35
|
spec.add_runtime_dependency 'i18n'
|
36
36
|
spec.add_runtime_dependency 'health_check', '~> 3.1'
|
37
|
+
spec.add_runtime_dependency 'rubocop', '= 1.25.1'
|
38
|
+
spec.add_runtime_dependency 'rubocop-rails', '= 2.13.2'
|
37
39
|
|
38
40
|
spec.add_development_dependency 'activerecord', '>= 4.2.0'
|
39
|
-
spec.add_development_dependency
|
41
|
+
spec.add_development_dependency 'appraisal'
|
40
42
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: modulorails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthieu Ciappara
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: railties
|
@@ -86,6 +86,34 @@ dependencies:
|
|
86
86
|
- - "~>"
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: '3.1'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rubocop
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - '='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 1.25.1
|
96
|
+
type: :runtime
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - '='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 1.25.1
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: rubocop-rails
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - '='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.13.2
|
110
|
+
type: :runtime
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - '='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 2.13.2
|
89
117
|
- !ruby/object:Gem::Dependency
|
90
118
|
name: activerecord
|
91
119
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,6 +157,7 @@ files:
|
|
129
157
|
- ".dockerignore"
|
130
158
|
- ".gitignore"
|
131
159
|
- ".rspec"
|
160
|
+
- ".rubocop.yml"
|
132
161
|
- ".travis.yml"
|
133
162
|
- Appraisals
|
134
163
|
- CHANGELOG.md
|
@@ -143,9 +172,12 @@ files:
|
|
143
172
|
- LICENSE.txt
|
144
173
|
- README.md
|
145
174
|
- Rakefile
|
175
|
+
- app/assets/stylesheets/modulorails.css
|
176
|
+
- app/helpers/modulorails/application_helper.rb
|
146
177
|
- bin/console
|
147
178
|
- bin/setup
|
148
179
|
- config/locales/en.yml
|
180
|
+
- docker-compose.debug.yml
|
149
181
|
- docker-compose.yml
|
150
182
|
- entrypoints/appraisal_test.sh
|
151
183
|
- gemfiles/rails_52.gemfile
|
@@ -167,11 +199,24 @@ files:
|
|
167
199
|
- lib/generators/modulorails/healthcheck/health_check_generator.rb
|
168
200
|
- lib/generators/modulorails/healthcheck/templates/.modulorails-health_check
|
169
201
|
- lib/generators/modulorails/healthcheck/templates/config/initializers/health_check.rb.tt
|
202
|
+
- lib/generators/modulorails/rubocop/rubocop_generator.rb
|
203
|
+
- lib/generators/modulorails/rubocop/templates/rubocop.yml.tt
|
170
204
|
- lib/generators/modulorails/self_update/self_update_generator.rb
|
205
|
+
- lib/generators/modulorails/service/service_generator.rb
|
206
|
+
- lib/generators/modulorails/service/templates/service.rb.tt
|
171
207
|
- lib/modulorails.rb
|
172
208
|
- lib/modulorails/configuration.rb
|
173
209
|
- lib/modulorails/data.rb
|
210
|
+
- lib/modulorails/error_data.rb
|
211
|
+
- lib/modulorails/errors/base_error.rb
|
212
|
+
- lib/modulorails/errors/errors.rb
|
213
|
+
- lib/modulorails/errors/invalid_format_error.rb
|
214
|
+
- lib/modulorails/errors/invalid_value_error.rb
|
174
215
|
- lib/modulorails/railtie.rb
|
216
|
+
- lib/modulorails/services/base_service.rb
|
217
|
+
- lib/modulorails/services/logs_for_method_service.rb
|
218
|
+
- lib/modulorails/services/services.rb
|
219
|
+
- lib/modulorails/success_data.rb
|
175
220
|
- lib/modulorails/validators/database_configuration.rb
|
176
221
|
- lib/modulorails/version.rb
|
177
222
|
- modulorails.gemspec
|