much-rails 0.1.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '08eb433f0b2e7fa04acaa6aec285a57d6c2988dc34067cb0ef401992a02125c4'
4
- data.tar.gz: dc1a2ddf9997e2aa2577a1100efedb52e4a3d89b2abc5ee6a8150626a5b42129
3
+ metadata.gz: f4834b7655c265c28bb9f46729491681daa8003dd99615c2074f27a010f22e10
4
+ data.tar.gz: 70603cc1bd5717a2edf35ff23cf2fa50ae35dc494431e2fbc476388af8c3d6a1
5
5
  SHA512:
6
- metadata.gz: d5178f432b4f6fd2712036e951742301b52565161e4d1387915315ea5a661beaa5b8d11935ae8eb7f761c76430f7afd1ba2243a0178d5fa7f155ac5a0751a934
7
- data.tar.gz: 7aa259619b17110e7ce6ec81defa760b2ae6494b2e4e1eb6677f9ba003d4ad4312f046ff806680c8bbed11b2af92a09d86640cec904be20627fec52870a2ae3b
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
@@ -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 gains.
16
- config.fingerprint_cache MuchRails::Assets::MemCache.new
17
-
18
- # Cache compiled content in memory in development/test for performance
19
- # gains since we aren't caching to the file system. Otherwise, don't
20
- # cache in memory as we are caching to the file system and won't benefit
21
- # from the in memory cache.
22
- much_rails_content_cache =
23
- if rails.env.development? || rails.env.test?
24
- MuchRails::Assets::MemCache.new
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 processing
37
- # on all .js and .scss files. Support compilation of .scss files.
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::Records::ValidateDestroy::DestructionInvalid => ex
21
+ rescue *MuchRails::DestroyService::ValidationErrors.exception_classes => ex
22
22
  set_the_return_value_for_the_call_method(
23
- MuchRails::Result.failure(
24
- exception: ex,
25
- validation_errors: ex&.destruction_errors.to_h,
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
@@ -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 any_breadcrumbs?
108
- breadcrumbs.any?
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
- class DestructionInvalid < StandardError
66
- attr_reader :record, :destruction_errors
70
+ def initialize(record = nil, field_name: :base)
71
+ super(record&.destruction_error_messages.to_a.join("\n"))
67
72
 
68
- def initialize(record = nil, field_name: :base)
69
- super(record&.destruction_error_messages.to_a.join("\n"))
73
+ @record = record
70
74
 
71
- @record = record
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
- messages = record&.destruction_error_messages.to_a
74
- @destruction_errors =
75
- if messages.any?
76
- { field_name.to_sym => messages }
77
- else
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
- end
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 ActiveRecord::RecordInvalid => ex
21
+ rescue *MuchRails::SaveService::ValidationErrors.exception_classes => ex
21
22
  set_the_return_value_for_the_call_method(
22
- MuchRails::Result.failure(
23
- record: ex.record,
24
- exception: ex,
25
- validation_errors: ex.record&.errors.to_h,
26
- validation_error_messages: ex.record&.errors&.full_messages.to_a,
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MuchRails
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.3"
5
5
  end
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.1"])
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