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 +4 -4
- data/lib/mime_actor/act.rb +72 -0
- data/lib/mime_actor/errors.rb +22 -0
- data/lib/mime_actor/rescue.rb +127 -0
- data/lib/mime_actor/scene.rb +54 -0
- data/lib/mime_actor/version.rb +3 -3
- data/lib/mime_actor.rb +5 -4
- metadata +6 -3
- data/lib/mime_actor/formatter.rb +0 -84
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ac518915a340b48be1aa4dbc91589df809cbbb297415fbc9ea1d2351e70e163
|
4
|
+
data.tar.gz: dde09568578db8acdf6f5b16d1a5e72f0b87750f954c6fb617e3b30ad29794c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/lib/mime_actor/version.rb
CHANGED
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 :
|
12
|
-
|
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
|
+
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-
|
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/
|
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
|
data/lib/mime_actor/formatter.rb
DELETED
@@ -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
|