mime_actor 0.0.4.alpha → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b353a4f0f8625adf8fc788c30a501bb8406ede3bb6ac9309d6e459fc829e0dbd
4
- data.tar.gz: e9f62e73020e5728e47ca3c6bd1386dc3b861261284c6bb55ce203cabf2d2a99
3
+ metadata.gz: 9ac518915a340b48be1aa4dbc91589df809cbbb297415fbc9ea1d2351e70e163
4
+ data.tar.gz: dde09568578db8acdf6f5b16d1a5e72f0b87750f954c6fb617e3b30ad29794c8
5
5
  SHA512:
6
- metadata.gz: 6dd846f06c7461b7c1762f3ff18b16798d41994731fd09a887452830a851b4642431ec115b7a9054cd17cdecd8b3be0d360bf1a1c5a43305e20fab50094c9b47
7
- data.tar.gz: 1a98f58e33535bd84d564a6c5413a1228cdaeecf0ae9f3203b822de60999623cbc412bc818a09c1b9878059149d7f564473b98a6abc07838862bde62ccaa0973
6
+ metadata.gz: c20053a6f4e13d78060b64980bb1bd128e3cfc0628692bc8c70f3e0a8201dbaa84ecd008f5500c3dd7a008eac6ff4bf2f5eb040e094d16dd816b1be087b33807
7
+ data.tar.gz: 280ef49bce9321ca1be252fa91978d6eb7b209dc14f56e1002ef01ccbccb70fc0bdba8fa2cfd3451b92343df5fc811a9d03a8ead4608726d66d4e543a433289c
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require 'active_support/configurable'
5
+ require "active_support/core_ext/module/attribute_accessors"
6
+ require 'abstract_controller/logger'
7
+ require 'abstract_controller/rendering'
8
+ require 'action_controller/metal/mime_responds'
9
+
10
+ module MimeActor
11
+ module Act
12
+ extend ActiveSupport::Concern
13
+ include ActiveSupport::Configurable
14
+ include AbstractController::Logger
15
+ include AbstractController::Rendering # required by MimeResponds
16
+ include ActionController::MimeResponds
17
+ include Scene
18
+ include Rescue
19
+
20
+ included do
21
+ mattr_accessor :raise_on_missing_actor, instance_writer: false, default: false
22
+ end
23
+
24
+ module ClassMethods
25
+ def dispatch_act(action: nil, format: nil, context: self, &block)
26
+ lambda do
27
+ context.instance_exec(&block)
28
+ rescue Exception => ex
29
+ respond_to?(:rescue_actor) && public_send(:rescue_actor, ex, action:, format:, context:) || raise
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def play_scene(action)
37
+ action = action&.to_sym
38
+ return unless acting_scenes.key?(action)
39
+
40
+ mime_types = acting_scenes.fetch(action, Set.new)
41
+ respond_to do |collector|
42
+ mime_types.each do |mime_type|
43
+ next unless actor = find_actor(action, mime_type)
44
+
45
+ dispatch = self.class.dispatch_act(
46
+ action: action,
47
+ format: mime_type,
48
+ context: self,
49
+ &actor
50
+ )
51
+ collector.public_send(mime_type, &dispatch)
52
+ end
53
+ end
54
+ end
55
+
56
+ def find_actor(action, mime_type)
57
+ actor_name = "#{action}_#{mime_type}"
58
+
59
+ if respond_to?(:action_methods) && public_send(:action_methods).include?(actor_name)
60
+ return self.method(actor_name)
61
+ end
62
+
63
+ error = MimeActor::ActorNotFound.new(action, mime_type)
64
+ unless self.raise_on_missing_actor
65
+ logger.warn { "Actor not found: #{error.inspect}" }
66
+ return
67
+ end
68
+
69
+ raise error
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MimeActor
4
+ class Error < StandardError
5
+ end
6
+
7
+ class ActorNotFound < Error
8
+ attr_reader :actor, :action, :format
9
+
10
+ def initialize(action, format)
11
+ @action = action
12
+ @format = format
13
+ @actor = "#{action}_#{format}"
14
+
15
+ super(":#{actor} not found")
16
+ end
17
+
18
+ def inspect
19
+ "<#{self.class.name}> actor:#{actor} not found for action:#{action}, format:#{format}"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require "active_support/core_ext/array/wrap"
5
+ require "active_support/core_ext/module/attribute_accessors"
6
+ require "active_support/core_ext/object/blank"
7
+ require "active_support/core_ext/string/inflections"
8
+ require "action_dispatch/http/mime_type"
9
+
10
+ module MimeActor
11
+ module Rescue
12
+ extend ActiveSupport::Concern
13
+
14
+ included do
15
+ mattr_accessor :actor_rescuers, instance_writer: false, default: []
16
+ end
17
+
18
+ module ClassMethods
19
+ def rescue_actor_from(*klazzes, action: nil, format: nil, with: nil, &block)
20
+ raise ArgumentError, "Error filter can't be empty" if klazzes.empty?
21
+ raise ArgumentError, "Provide only the with: keyword argument or a block" if with.present? && block_given?
22
+ raise ArgumentError, "Provide the with: keyword argument or a block" unless with.present? || block_given?
23
+
24
+ with = block if block_given?
25
+ raise ArgumentError, "Rescue handler can only be Symbol/Proc" unless with.is_a?(Proc) || with.is_a?(Symbol)
26
+
27
+ if format.present?
28
+ case format
29
+ when Symbol
30
+ raise ArgumentError, "Unsupported format: #{format}" unless Mime::SET.symbols.include?(format.to_sym)
31
+ when Enumerable
32
+ unfiltered = format.to_set
33
+ filtered = unfiltered & Mime::SET.symbols.to_set
34
+ rejected = unfiltered - filtered
35
+ raise ArgumentError, "Unsupported formats: #{rejected.join(', ')}" if rejected.size.positive?
36
+ else
37
+ raise ArgumentError, "Format filter can only be Symbol/Enumerable"
38
+ end
39
+ end
40
+
41
+ if action.present? && !(action.is_a?(Symbol) || action.is_a?(Enumerable))
42
+ raise ArgumentError, "Action filter can only be Symbol/Enumerable"
43
+ end
44
+
45
+ klazzes.each do |klazz|
46
+ error = if klazz.is_a?(Module)
47
+ klazz.name
48
+ elsif klazz.is_a?(String)
49
+ klazz
50
+ else
51
+ raise ArgumentError, "#{klazz.inspect} must be a class/module or a String referencing a class/module"
52
+ end
53
+
54
+ # append at the end because strategies are read in reverse.
55
+ self.actor_rescuers << [error, format, action, with]
56
+ end
57
+ end
58
+
59
+ def rescue_actor(error, action: nil, format: nil, context: self, visited: [])
60
+ visited << error
61
+
62
+ if rescuer = dispatch_rescuer(error, format:, action:, context:)
63
+ rescuer.call(error, format, action)
64
+ error
65
+ elsif error && error.cause && !visited.include?(error.cause)
66
+ rescue_actor(error.cause, format:, action:, context:, visited:)
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def dispatch_rescuer(error, format:, action:, context:)
73
+ case rescuer = find_rescuer(error, format:, action:)
74
+ when Symbol
75
+ rescuer_method = context.method(rescuer)
76
+ case rescuer_method.arity
77
+ when 0
78
+ -> e,f,a { rescuer_method.call }
79
+ when 1
80
+ -> e,f,a { rescuer_method.call(e) }
81
+ when 2
82
+ -> e,f,a { rescuer_method.call(e,f) }
83
+ else
84
+ -> e,f,a { rescuer_method.call(e,f,a) }
85
+ end
86
+ when Proc
87
+ case rescuer.arity
88
+ when 0
89
+ -> e,f,a { context.instance_exec(&rescuer) }
90
+ when 1
91
+ -> e,f,a { context.instance_exec(e, &rescuer) }
92
+ when 2
93
+ -> e,f,a { context.instance_exec(e, f, &rescuer) }
94
+ else
95
+ -> e,f,a { context.instance_exec(e, f, a, &rescuer) }
96
+ end
97
+ end
98
+ end
99
+
100
+ def find_rescuer(error, format:, action:)
101
+ return unless error
102
+
103
+ *_, rescuer = actor_rescuers.reverse_each.detect do |rescuee, format_filter, action_filter|
104
+ next if action_filter.present? && !Array.wrap(action_filter).include?(action)
105
+ next if format_filter.present? && !Array.wrap(format_filter).include?(format)
106
+ next unless klazz = constantize_rescuee(rescuee)
107
+
108
+ klazz === error # klazz is a member of error
109
+ end
110
+ rescuer
111
+ end
112
+
113
+ def constantize_rescuee(class_or_name)
114
+ case class_or_name
115
+ when String, Symbol
116
+ begin
117
+ const_get(class_or_name)
118
+ rescue NameError
119
+ class_or_name.safe_constantize
120
+ end
121
+ else
122
+ class_or_name
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+ require "active_support/core_ext/array/extract_options"
5
+ require "active_support/core_ext/array/wrap"
6
+ require "active_support/core_ext/hash/indifferent_access"
7
+ require "active_support/core_ext/module/attribute_accessors"
8
+ require "action_dispatch/http/mime_type"
9
+
10
+ module MimeActor
11
+ module Scene
12
+ extend ActiveSupport::Concern
13
+
14
+ included do
15
+ mattr_accessor :acting_scenes, instance_writer: false, default: ActiveSupport::HashWithIndifferentAccess.new
16
+ end
17
+
18
+ module ClassMethods
19
+ def compose_scene(*options)
20
+ config = options.extract_options!
21
+ actions = Array.wrap(config[:on])
22
+
23
+ if actions.empty?
24
+ raise ArgumentError, "Action name can't be empty, e.g. on: :create"
25
+ end
26
+
27
+ options.each do |mime_type|
28
+ unless Mime::SET.symbols.include?(mime_type.to_sym)
29
+ raise ArgumentError, "Unsupported format: #{mime_type}"
30
+ end
31
+
32
+ actions.each do |action|
33
+ unless acting_scenes.key?(action)
34
+ if respond_to?(:action_methods) && public_send(:action_methods).include?(action.to_s)
35
+ raise ArgumentError, "Action method already defined: #{action}"
36
+ end
37
+ end
38
+
39
+ acting_scenes[action] ||= Set.new
40
+ acting_scenes[action] |= [mime_type.to_sym]
41
+
42
+ unless respond_to?(action)
43
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
44
+ def #{action}
45
+ respond_to?(:play_scene) && public_send(:play_scene, :#{action})
46
+ end
47
+ RUBY
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -11,9 +11,9 @@ module MimeActor
11
11
 
12
12
  module VERSION
13
13
  MAJOR = 0
14
- MINOR = 0
15
- BUILD = 4
16
- PRE = "alpha"
14
+ MINOR = 2
15
+ BUILD = 0
16
+ PRE = nil
17
17
 
18
18
  STRING = [MAJOR, MINOR, BUILD, PRE].compact.join(".")
19
19
  end
data/lib/mime_actor.rb CHANGED
@@ -1,13 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "mime_actor/version"
4
+ require_relative "mime_actor/errors"
4
5
 
5
- require 'active_support'
6
+ require 'active_support/dependencies/autoload'
6
7
 
7
8
  module MimeActor
8
- extend ActiveSupport::Concern
9
9
  extend ActiveSupport::Autoload
10
10
 
11
- autoload :Formatter
12
- include Formatter
11
+ autoload :Act
12
+ autoload :Scene
13
+ autoload :Rescue
13
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mime_actor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4.alpha
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Chang
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-22 00:00:00.000000000 Z
11
+ date: 2024-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -94,7 +94,10 @@ files:
94
94
  - README.md
95
95
  - Rakefile
96
96
  - lib/mime_actor.rb
97
- - lib/mime_actor/formatter.rb
97
+ - lib/mime_actor/act.rb
98
+ - lib/mime_actor/errors.rb
99
+ - lib/mime_actor/rescue.rb
100
+ - lib/mime_actor/scene.rb
98
101
  - lib/mime_actor/version.rb
99
102
  - sig/mime_actor.rbs
100
103
  homepage: https://github.com/ryancyq/mime_actor
@@ -1,84 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'active_support'
4
- require "active_support/core_ext/array/extract_options"
5
- require "active_support/core_ext/array/wrap"
6
- require "active_support/core_ext/hash/indifferent_access"
7
- require "active_support/core_ext/module/attribute_accessors"
8
- require 'action_controller'
9
-
10
- module MimeActor
11
- module Formatter
12
- extend ActiveSupport::Concern
13
- include ActiveSupport::Configurable
14
- include AbstractController::Logger
15
- include AbstractController::Rendering # required by MimeResponds
16
- include ActionController::MimeResponds
17
-
18
- SUPPORTED_MIME_TYPES = Mime::SET.symbols.to_set
19
- private_constant :SUPPORTED_MIME_TYPES
20
-
21
- included do
22
- mattr_accessor :action_formatters, instance_writer: false, default: ActiveSupport::HashWithIndifferentAccess.new
23
- mattr_accessor :raise_on_missing_action_formatter, instance_writer: false, default: false
24
- end
25
-
26
- module ClassMethods
27
- def act_on_format(*formats_and_options)
28
- options = formats_and_options.extract_options!
29
- actions = Array.wrap(options[:on])
30
-
31
- message = "Action name can't be blank, e.g. on: :create"
32
- raise ArgumentError, message if actions.empty?
33
-
34
- formats_and_options.each do |format|
35
- message = "Unsupported format: #{format}"
36
- raise ArgumentError, message unless SUPPORTED_MIME_TYPES.include?(format.to_sym)
37
-
38
- actions.each do |action|
39
- message = "Action method already defined: #{action}"
40
- raise ArgumentError, message if action_methods.include?(action.to_s) && !action_formatters.key?(action)
41
-
42
- action_formatters[action] ||= Set.new
43
- action_formatters[action] |= [format]
44
-
45
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
46
- def #{action}
47
- perform_act
48
- end
49
- RUBY
50
- end
51
- end
52
- end
53
- end
54
-
55
- private
56
-
57
- def perform_act
58
- return unless action_formatters.key?(action_name)
59
-
60
- formatters = action_formatters.fetch(action_name, Set.new)
61
- respond_to do |collector|
62
- formatters.each do |formatter|
63
- next unless actor = find_actor(action_name, formatter)
64
-
65
- collector.public_send(formatter, &actor)
66
- end
67
- end
68
- end
69
-
70
- def find_actor(action, format)
71
- actor_name = "#{action}_#{format}"
72
-
73
- unless action_methods.include?(actor_name)
74
- message = "Method: #{actor_name} could not be found for action: #{action_name}, format: #{format}"
75
- raise AbstractController::ActionNotFound.new(message, self, actor_name) if raise_on_missing_action_formatter
76
-
77
- logger.warn { message }
78
- return
79
- end
80
-
81
- self.method(actor_name)
82
- end
83
- end
84
- end