much-rails 0.1.2 → 0.2.3
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/lib/much-rails.rb +9 -0
- data/lib/much-rails/abstract_class.rb +38 -0
- data/lib/much-rails/assets.rb +33 -18
- data/lib/much-rails/destroy_service.rb +42 -5
- data/lib/much-rails/layout.rb +29 -2
- data/lib/much-rails/records/validate_destroy.rb +23 -13
- data/lib/much-rails/save_service.rb +44 -7
- data/lib/much-rails/service_validation_errors.rb +41 -0
- data/lib/much-rails/version.rb +1 -1
- data/much-rails.gemspec +1 -1
- data/test/unit/abstract_class_tests.rb +45 -0
- data/test/unit/assets_tests.rb +46 -14
- data/test/unit/destroy_service_tests.rb +165 -21
- data/test/unit/layout_tests.rb +24 -18
- data/test/unit/much-rails_tests.rb +42 -1
- data/test/unit/records/always_destroyable_tests.rb +1 -1
- data/test/unit/records/not_destroyable_tests.rb +1 -1
- data/test/unit/records/validate_destroy_tests.rb +84 -4
- data/test/unit/save_service_tests.rb +180 -20
- data/test/unit/service_validation_errors_tests.rb +107 -0
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4834b7655c265c28bb9f46729491681daa8003dd99615c2074f27a010f22e10
|
4
|
+
data.tar.gz: 70603cc1bd5717a2edf35ff23cf2fa50ae35dc494431e2fbc476388af8c3d6a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3e614697001a8f9dd5d09a78dcfa3c5e4f6fb458406a7af6c018d7a829687b74c1a98c673ef3e62ec8f7a28f85e4a59f4b3bd7c01ccb95b4ff0d661f569cb61
|
7
|
+
data.tar.gz: dcd670e7ca04b1394a92a8a9a7a55b31fd825f5f99bf0282a18d4907e7f614536f30195756ceda6b4320a2fec0d073caf96ab5b74aa57428e4331a1c73381cd7
|
data/lib/much-rails.rb
CHANGED
@@ -4,6 +4,7 @@ require "active_support"
|
|
4
4
|
require "active_support/core_ext"
|
5
5
|
|
6
6
|
require "much-rails/version"
|
7
|
+
require "much-rails/abstract_class"
|
7
8
|
require "much-rails/action"
|
8
9
|
require "much-rails/boolean"
|
9
10
|
require "much-rails/call_method"
|
@@ -46,6 +47,14 @@ module MuchRails
|
|
46
47
|
add_instance_config :action, method_name: :action
|
47
48
|
add_instance_config :layout, method_name: :layout
|
48
49
|
|
50
|
+
def add_save_service_validation_error(exception_class, &block)
|
51
|
+
MuchRails::SaveService::ValidationErrors.add(exception_class, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_destroy_service_validation_error(exception_class, &block)
|
55
|
+
MuchRails::DestroyService::ValidationErrors.add(exception_class, &block)
|
56
|
+
end
|
57
|
+
|
49
58
|
class ActionConfig
|
50
59
|
attr_accessor :namespace
|
51
60
|
attr_accessor :sanitized_exception_classes
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "much-rails/mixin"
|
4
|
+
|
5
|
+
# MuchRails::AbstractClass overrides the `new` class method to prevent a class
|
6
|
+
# from being instantiated directly.
|
7
|
+
module MuchRails::AbstractClass
|
8
|
+
include MuchRails::Mixin
|
9
|
+
|
10
|
+
after_mixin_included do
|
11
|
+
self.abstract_class = self
|
12
|
+
|
13
|
+
define_singleton_method(:new) do |*args, &block|
|
14
|
+
if abstract_class?
|
15
|
+
raise(
|
16
|
+
NotImplementedError,
|
17
|
+
"#{self} is an abstract class and cannot be instantiated.",
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
super(*args, &block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
mixin_class_methods do
|
26
|
+
def abstract_class
|
27
|
+
@abstract_class
|
28
|
+
end
|
29
|
+
|
30
|
+
def abstract_class=(value)
|
31
|
+
@abstract_class = value
|
32
|
+
end
|
33
|
+
|
34
|
+
def abstract_class?
|
35
|
+
!!(abstract_class == self)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/much-rails/assets.rb
CHANGED
@@ -12,20 +12,16 @@ end
|
|
12
12
|
module MuchRails::Assets
|
13
13
|
def self.configure_for_rails(rails)
|
14
14
|
MuchRails::Assets.configure do |config|
|
15
|
-
# Cache fingerprints in memory for performance
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
else
|
26
|
-
MuchRails::Assets::NoCache.new
|
27
|
-
end
|
28
|
-
config.content_cache much_rails_content_cache
|
15
|
+
# Cache fingerprints/content in memory in non-development for performance
|
16
|
+
# gains. Don't cache in memory in developlemnt so changes are reflected
|
17
|
+
# without restarting the server.
|
18
|
+
if !rails.env.development?
|
19
|
+
config.fingerprint_cache MuchRails::Assets::MemCache.new
|
20
|
+
config.content_cache MuchRails::Assets::MemCache.new
|
21
|
+
else
|
22
|
+
config.fingerprint_cache MuchRails::Assets::NoCache.new
|
23
|
+
config.content_cache MuchRails::Assets::NoCache.new
|
24
|
+
end
|
29
25
|
|
30
26
|
# Cache out compiled file content to the public folder in non
|
31
27
|
# development/test environments.
|
@@ -33,21 +29,40 @@ module MuchRails::Assets
|
|
33
29
|
config.file_store rails.root.join("public")
|
34
30
|
end
|
35
31
|
|
36
|
-
# Look for asset files in the app/assets folder. Support ERB
|
37
|
-
# on all .
|
38
|
-
config.source rails.root.join("app", "assets") do |s|
|
32
|
+
# Look for asset files in the app/assets/css folder. Support ERB
|
33
|
+
# on all .scss files. Support compilation of .scss files.
|
34
|
+
config.source rails.root.join("app", "assets", "css") do |s|
|
35
|
+
s.base_path "css"
|
36
|
+
|
39
37
|
# Reject SCSS partials
|
40
38
|
s.filter do |paths|
|
41
39
|
paths.reject{ |p| File.basename(p) =~ /^_.*\.scss$/ }
|
42
40
|
end
|
43
41
|
|
44
|
-
s.engine "js", MuchRails::Assets::Erubi::Engine
|
45
42
|
s.engine "scss", MuchRails::Assets::Erubi::Engine
|
46
43
|
s.engine "scss", MuchRails::Assets::Sass::Engine, {
|
47
44
|
syntax: "scss",
|
48
45
|
output_style: "compressed",
|
49
46
|
}
|
50
47
|
end
|
48
|
+
|
49
|
+
# Look for asset files in the app/assets/img folder.
|
50
|
+
config.source rails.root.join("app", "assets", "img") do |s|
|
51
|
+
s.base_path "img"
|
52
|
+
end
|
53
|
+
|
54
|
+
# Look for asset files in the app/assets/js folder. Support ERB
|
55
|
+
# on all .js files.
|
56
|
+
config.source rails.root.join("app", "assets", "js") do |s|
|
57
|
+
s.base_path "js"
|
58
|
+
|
59
|
+
s.engine "js", MuchRails::Assets::Erubi::Engine
|
60
|
+
end
|
61
|
+
|
62
|
+
# Look for asset files in the app/assets/vendor folder
|
63
|
+
config.source rails.root.join("app", "assets", "vendor") do |s|
|
64
|
+
s.base_path "vendor"
|
65
|
+
end
|
51
66
|
end
|
52
67
|
MuchRails::Assets.init
|
53
68
|
end
|
@@ -18,12 +18,49 @@ module MuchRails::DestroyService
|
|
18
18
|
|
19
19
|
around_call do |receiver|
|
20
20
|
receiver.call
|
21
|
-
rescue MuchRails::
|
21
|
+
rescue *MuchRails::DestroyService::ValidationErrors.exception_classes => ex
|
22
22
|
set_the_return_value_for_the_call_method(
|
23
|
-
MuchRails::
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
MuchRails::DestroyService::ValidationErrors.result_for(ex),
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module ValidationErrors
|
29
|
+
def self.add(exception_class, &block)
|
30
|
+
service_validation_errors.add(exception_class, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.exception_classes
|
34
|
+
service_validation_errors.exception_classes
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.result_for(ex)
|
38
|
+
service_validation_errors.result_for(ex)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.service_validation_errors
|
42
|
+
@service_validation_errors ||=
|
43
|
+
MuchRails::ServiceValidationErrors
|
44
|
+
.new
|
45
|
+
.tap do |e|
|
46
|
+
e.add(MuchRails::Records::DestructionInvalid) do |ex|
|
47
|
+
MuchRails::DestroyService::FailureResult.new(
|
48
|
+
record: ex.record,
|
49
|
+
exception: ex,
|
50
|
+
validation_errors: ex.errors.to_h,
|
51
|
+
validation_error_messages: ex.error_full_messages.to_a,
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
module FailureResult
|
59
|
+
def self.new(exception:, validation_errors:, **kargs)
|
60
|
+
MuchResult.failure(
|
61
|
+
exception: exception,
|
62
|
+
validation_errors: validation_errors,
|
63
|
+
**kargs,
|
27
64
|
)
|
28
65
|
end
|
29
66
|
end
|
data/lib/much-rails/layout.rb
CHANGED
@@ -50,6 +50,14 @@ module MuchRails::Layout
|
|
50
50
|
@javascripts ||= []
|
51
51
|
end
|
52
52
|
|
53
|
+
def head_link(url, **attributes)
|
54
|
+
head_links << HeadLink.new(url, **attributes)
|
55
|
+
end
|
56
|
+
|
57
|
+
def head_links
|
58
|
+
@head_links ||= []
|
59
|
+
end
|
60
|
+
|
53
61
|
def layout(value)
|
54
62
|
layouts << value
|
55
63
|
end
|
@@ -104,12 +112,31 @@ module MuchRails::Layout
|
|
104
112
|
@javascripts ||= self.class.javascripts.map(&:call)
|
105
113
|
end
|
106
114
|
|
107
|
-
def
|
108
|
-
|
115
|
+
def head_links
|
116
|
+
@head_links ||= self.class.head_links
|
109
117
|
end
|
110
118
|
|
111
119
|
def layouts
|
112
120
|
self.class.layouts
|
113
121
|
end
|
122
|
+
|
123
|
+
def any_breadcrumbs?
|
124
|
+
breadcrumbs.any?
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class HeadLink
|
129
|
+
attr_reader :href, :attributes
|
130
|
+
|
131
|
+
def initialize(href, **attributes)
|
132
|
+
@href = href
|
133
|
+
@attributes = attributes
|
134
|
+
end
|
135
|
+
|
136
|
+
def ==(other)
|
137
|
+
super unless other.is_a?(self.class)
|
138
|
+
|
139
|
+
href == other.href && attributes == other.attributes
|
140
|
+
end
|
114
141
|
end
|
115
142
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_model/error"
|
3
4
|
require "much-rails/mixin"
|
4
5
|
|
5
6
|
module MuchRails; end
|
@@ -33,7 +34,7 @@ module MuchRails::Records::ValidateDestroy
|
|
33
34
|
|
34
35
|
def destroy!(as: :base, validate: true)
|
35
36
|
if validate && !destroyable?
|
36
|
-
raise DestructionInvalid.new(self, field_name: as)
|
37
|
+
raise MuchRails::Records::DestructionInvalid.new(self, field_name: as)
|
37
38
|
end
|
38
39
|
|
39
40
|
# `_raise_record_not_destroyed` is from ActiveRecord. This logic was
|
@@ -61,22 +62,31 @@ module MuchRails::Records::ValidateDestroy
|
|
61
62
|
raise NotImplementedError
|
62
63
|
end
|
63
64
|
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class MuchRails::Records::DestructionInvalid < StandardError
|
68
|
+
attr_reader :record, :errors, :error_full_messages
|
64
69
|
|
65
|
-
|
66
|
-
|
70
|
+
def initialize(record = nil, field_name: :base)
|
71
|
+
super(record&.destruction_error_messages.to_a.join("\n"))
|
67
72
|
|
68
|
-
|
69
|
-
super(record&.destruction_error_messages.to_a.join("\n"))
|
73
|
+
@record = record
|
70
74
|
|
71
|
-
|
75
|
+
messages = record&.destruction_error_messages.to_a
|
76
|
+
@errors =
|
77
|
+
if messages.any?
|
78
|
+
{ field_name.to_sym => messages }
|
79
|
+
else
|
80
|
+
{}
|
81
|
+
end
|
72
82
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
83
|
+
@error_full_messages =
|
84
|
+
if field_name == :base
|
85
|
+
messages
|
86
|
+
else
|
87
|
+
messages.map do |m|
|
88
|
+
ActiveModel::Error.new(@record, field_name, m).full_message
|
79
89
|
end
|
80
|
-
|
90
|
+
end
|
81
91
|
end
|
82
92
|
end
|
@@ -4,6 +4,7 @@ require "active_record"
|
|
4
4
|
require "much-rails/mixin"
|
5
5
|
require "much-rails/result"
|
6
6
|
require "much-rails/service"
|
7
|
+
require "much-rails/service_validation_errors"
|
7
8
|
|
8
9
|
module MuchRails; end
|
9
10
|
|
@@ -17,14 +18,50 @@ module MuchRails::SaveService
|
|
17
18
|
|
18
19
|
around_call do |receiver|
|
19
20
|
receiver.call
|
20
|
-
rescue
|
21
|
+
rescue *MuchRails::SaveService::ValidationErrors.exception_classes => ex
|
21
22
|
set_the_return_value_for_the_call_method(
|
22
|
-
MuchRails::
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
MuchRails::SaveService::ValidationErrors.result_for(ex),
|
24
|
+
)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module ValidationErrors
|
29
|
+
def self.add(exception_class, &block)
|
30
|
+
service_validation_errors.add(exception_class, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.exception_classes
|
34
|
+
service_validation_errors.exception_classes
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.result_for(ex)
|
38
|
+
service_validation_errors.result_for(ex)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.service_validation_errors
|
42
|
+
@service_validation_errors ||=
|
43
|
+
MuchRails::ServiceValidationErrors
|
44
|
+
.new
|
45
|
+
.tap do |e|
|
46
|
+
e.add(ActiveRecord::RecordInvalid) do |ex|
|
47
|
+
MuchRails::SaveService::FailureResult.new(
|
48
|
+
record: ex.record,
|
49
|
+
exception: ex,
|
50
|
+
validation_errors: ex.record&.errors.to_h,
|
51
|
+
validation_error_messages:
|
52
|
+
ex.record&.errors&.full_messages.to_a,
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
module FailureResult
|
60
|
+
def self.new(exception:, validation_errors:, **kargs)
|
61
|
+
MuchResult.failure(
|
62
|
+
exception: exception,
|
63
|
+
validation_errors: validation_errors,
|
64
|
+
**kargs,
|
28
65
|
)
|
29
66
|
end
|
30
67
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MuchRails; end
|
4
|
+
|
5
|
+
class MuchRails::ServiceValidationErrors
|
6
|
+
attr_reader :hash
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@hash = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(exception_class, &block)
|
13
|
+
unless exception_class && exception_class < Exception
|
14
|
+
raise(ArgumentError, "#{exception_class} is not an Exception")
|
15
|
+
end
|
16
|
+
|
17
|
+
@hash[exception_class] = block
|
18
|
+
end
|
19
|
+
|
20
|
+
def exception_classes
|
21
|
+
@hash.keys
|
22
|
+
end
|
23
|
+
|
24
|
+
def result_for(ex)
|
25
|
+
result_proc = nil
|
26
|
+
exception_class = ex.class
|
27
|
+
loop do
|
28
|
+
result_proc = @hash[exception_class]
|
29
|
+
break unless result_proc.nil?
|
30
|
+
|
31
|
+
exception_class =
|
32
|
+
if exception_class.superclass.nil?
|
33
|
+
raise ArgumentError, "#{ex.class} hasn't been configured"
|
34
|
+
else
|
35
|
+
exception_class.superclass
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
result_proc.call(ex)
|
40
|
+
end
|
41
|
+
end
|
data/lib/much-rails/version.rb
CHANGED
data/much-rails.gemspec
CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |gem|
|
|
29
29
|
|
30
30
|
gem.add_dependency("activerecord", ["> 5.0", "< 7.0"])
|
31
31
|
gem.add_dependency("activesupport", ["> 5.0", "< 7.0"])
|
32
|
-
gem.add_dependency("dassets", ["~> 0.15.
|
32
|
+
gem.add_dependency("dassets", ["~> 0.15.2"])
|
33
33
|
gem.add_dependency("dassets-erubi", ["~> 0.1.1"])
|
34
34
|
gem.add_dependency("dassets-sass", ["~> 0.5.1"])
|
35
35
|
gem.add_dependency("much-boolean", ["~> 0.2.1"])
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "assert"
|
4
|
+
require "much-rails/abstract_class"
|
5
|
+
|
6
|
+
module MuchRails::AbstractClassTest
|
7
|
+
class UnitTests < Assert::Context
|
8
|
+
desc "MuchRails::AbstractClass"
|
9
|
+
subject{ unit_class }
|
10
|
+
|
11
|
+
let(:unit_class){ MuchRails::AbstractClass }
|
12
|
+
|
13
|
+
should "include MuchRails::Mixin" do
|
14
|
+
assert_that(subject).includes(MuchRails::Mixin)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class ReceiverTests < UnitTests
|
19
|
+
desc "receiver"
|
20
|
+
subject{ receiver_class }
|
21
|
+
|
22
|
+
let(:receiver_class) do
|
23
|
+
Class.new.tap{ |c| c.include unit_class }
|
24
|
+
end
|
25
|
+
let(:receiver_subclass) do
|
26
|
+
Class.new(receiver_class)
|
27
|
+
end
|
28
|
+
|
29
|
+
should have_accessor :abstract_class
|
30
|
+
should have_imeths :new, :abstract_class?
|
31
|
+
|
32
|
+
should "know if it is an abstract class or not" do
|
33
|
+
assert_that(subject.abstract_class?).equals(true)
|
34
|
+
assert_that(receiver_subclass.abstract_class?).equals(false)
|
35
|
+
end
|
36
|
+
|
37
|
+
should "prevent calling .new on the receiver" do
|
38
|
+
assert_that{ subject.new }.raises(NotImplementedError)
|
39
|
+
end
|
40
|
+
|
41
|
+
should "allow calling .new on subclasses of the receiver" do
|
42
|
+
assert_that(receiver_subclass.new).is_a?(subject)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|