mime_actor 0.6.4 → 0.7.1

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.
@@ -5,36 +5,29 @@
5
5
  require "mime_actor/errors"
6
6
  require "mime_actor/validator"
7
7
 
8
+ require "active_support/code_generator"
8
9
  require "active_support/concern"
9
- require "active_support/core_ext/array/wrap"
10
10
  require "active_support/core_ext/module/attribute_accessors"
11
- require "active_support/core_ext/object/blank"
12
11
 
13
12
  module MimeActor
14
13
  # # MimeActor Scene
15
14
  #
16
15
  # Scene provides configuration for `action` + `format` definitions
17
16
  #
18
- # @example register a `html` format on action `index`
19
- # respond_act_to :html, on: :index
17
+ # @example register a `html` format on action `create`
18
+ # act_on_action :create, format: :html
20
19
  #
21
- # # this method should be defined in the class
22
- # def index_html; end
23
20
  # @example register `html`, `json` formats on actions `index`, `show`
24
- # respond_act_to :html, :json , on: [:index, :show]
21
+ # act_on_action :index, :show, format: [:html, :json]
25
22
  #
26
- # # these methods should be defined in the class
27
- # def index_html; end
28
- # def index_json; end
29
- # def show_html; end
30
- # def show_json; end
31
23
  # @example register a `html` format on action `index` with respond handler method
32
- # respond_act_to :html, on: :index, with: :render_html
24
+ # act_on_action :index, format: :html, with: :render_html
33
25
  #
34
- # # this method should be defined in the class
35
- # def render_html; end
36
- # @example register a `html` format on action `index` with respond handler proc
37
- # respond_act_to :html, on: :index do
26
+ # @example register a `html` format on action `index` with respond handler Proc
27
+ # act_on_action :index, format: :html, with: -> { render html: "<h1>my header</h1>" }
28
+ #
29
+ # @example register a `html` format on action `index` with respond handler block
30
+ # act_on_action :html, on: :index do
38
31
  # render :index
39
32
  # end
40
33
  #
@@ -52,78 +45,94 @@ module MimeActor
52
45
  module ClassMethods
53
46
  # Register `action` + `format` definitions.
54
47
  #
55
- # @param formats the collection of `format`
56
- # @param on the collection of `action`
57
- # @param with the respond handler when `block` is not provided
58
- # @param block the `block` to evaluate when `with` is not provided
48
+ # @param action a collection of `action`
49
+ # @param format a single `format` or a collection of `format`
50
+ # @param with the respond handler for the given `action` + `format`
51
+ # @param block the `block` to be yieled for the given `action` + `format`
59
52
  #
60
- # @example register a `html` format on action `index`
61
- # respond_act_to :html, on: :index
53
+ # @example register a `html` format on action `create`
54
+ # act_on_action :create, format: :html
62
55
  #
63
- # # this method should be defined in the class
64
- # def index_html; end
56
+ # # an action method will be defined in the class
57
+ # def indexl; end
65
58
  # @example register `html`, `json` formats on actions `index`, `show`
66
- # respond_act_to :html, :json , on: [:index, :show]
59
+ # act_on_action :index, :show, format: [:html, :json]
67
60
  #
68
- # # these methods should be defined in the class
69
- # def index_html; end
70
- # def index_json; end
71
- # def show_html; end
72
- # def show_json; end
61
+ # # these action methods will be defined in the class
62
+ # def index; end
63
+ # def show; end
73
64
  # @example register a `html` format on action `index` with respond handler method
74
- # respond_act_to :html, on: :index, with: :render_html
65
+ # act_on_action :index, format: :html, with: :render_html
75
66
  #
76
- # # this method should be defined in the class
67
+ # # an action method will be defined in the class
68
+ # # the handler method will be called by the action method.
69
+ # def index; end
70
+ # # the given method should be defined in the class
77
71
  # def render_html; end
78
- # @example register a `html` format on action `index` with respond handler proc
79
- # respond_act_to :html, on: :index do
72
+ # @example register a `html` format on action `index` with respond handler Proc
73
+ # act_on_action :index, format: :html, with: -> { render html: "<h1>my header</h1>" }
74
+ #
75
+ # # an action method will be defined in the class,
76
+ # # the handler Proc will be called by the action method.
77
+ # def index; end
78
+ # @example register a `html` format on action `index` with respond handler block
79
+ # act_on_action :html, on: :index do
80
80
  # render :index
81
81
  # end
82
82
  #
83
- # For each unique `action` being registered, it will have a corresponding `action` method being defined.
84
- def respond_act_to(*formats, on: nil, with: nil, &block)
85
- validate!(:formats, formats)
86
-
87
- raise ArgumentError, "provide either with: or a block" if with.present? && block_given?
88
-
89
- validate!(:callable, with) if with.present?
83
+ # # an action method will be defined in the class
84
+ # def index; end
85
+ #
86
+ # For each unique `action` being registered, a corresponding `action` method will be defined.
87
+ def act_on_action(*actions, format:, with: nil, &block)
88
+ raise ArgumentError, "format is required" if format.nil?
89
+ raise ArgumentError, "provide either with: or a block" if !with.nil? && block_given?
90
+
91
+ validate!(:actions, actions)
92
+ validate!(:format_or_formats, format)
93
+ validate!(:callable, with) unless with.nil?
90
94
  with = block if block_given?
91
95
 
92
- raise ArgumentError, "action is required" unless (actions = on).present?
93
-
94
- validate!(:action_or_actions, actions)
95
-
96
- Array.wrap(actions).each do |action|
97
- formats.each { |format| compose_scene(action, format, with) }
96
+ actions.each do |action|
97
+ acting_scenes[action] ||= {}
98
+ Array(format).each do |action_format|
99
+ acting_scenes[action][action_format] = with
100
+ end
98
101
  end
99
- end
100
102
 
101
- # TODO: remove on next breaking change release
102
- alias act_on_format respond_act_to
103
+ generate_action_methods(actions)
104
+ end
103
105
 
104
106
  private
105
107
 
106
- def compose_scene(action, format, actor)
107
- action_defined = (instance_methods + private_instance_methods).include?(action.to_sym)
108
- raise MimeActor::ActionExisted, action if !acting_scenes.key?(action) && action_defined
109
-
110
- acting_scenes[action] ||= {}
111
- acting_scenes[action][format] = actor
108
+ def generated_action_methods
109
+ @generated_action_methods ||= Module.new.tap { |mod| prepend mod }
110
+ end
112
111
 
113
- define_scene(action) unless action_defined
112
+ def generate_action_methods(actions)
113
+ ActiveSupport::CodeGenerator.batch(generated_action_methods, __FILE__, __LINE__) do |owner|
114
+ actions.each do |action|
115
+ owner.define_cached_method(action, namespace: :mime_scene) do |batch|
116
+ batch << action_method_template(action)
117
+ end
118
+ end
119
+ end
114
120
  end
115
121
 
116
- def define_scene(action)
117
- module_eval(
118
- # def index
119
- # self.respond_to?(:start_scene) && self.start_scene
120
- # end
121
- <<-RUBY, __FILE__, __LINE__ + 1
122
- def #{action}
123
- self.respond_to?(:start_scene) && self.start_scene
122
+ def action_method_template(action)
123
+ <<-RUBY
124
+ def #{action}
125
+ if respond_to?(:start_scene)
126
+ start_scene do
127
+ raise #{MimeActor::ActionNotImplemented}, :#{action} unless defined?(super)
128
+ super
129
+ end
130
+ else
131
+ raise #{MimeActor::ActionNotImplemented}, :#{action} unless defined?(super)
132
+ super
124
133
  end
125
- RUBY
126
- )
134
+ end
135
+ RUBY
127
136
  end
128
137
  end
129
138
  end
@@ -26,50 +26,6 @@ module MimeActor
26
26
  mattr_accessor :raise_on_actor_error, instance_writer: false, default: false
27
27
  end
28
28
 
29
- module ClassMethods
30
- # Determine if the `actor_name` belongs to a public instance method excluding methods inherited from ancestors
31
- #
32
- # @param actor_name
33
- #
34
- def actor?(actor_name)
35
- # exclude public methods from ancestors
36
- found = public_instance_methods(false).include?(actor_name.to_sym)
37
- # exclude private methods from ancestors
38
- if !found && private_instance_methods(false).include?(actor_name.to_sym)
39
- logger.debug { "actor must be public method, #{actor_name} is private method" }
40
- end
41
- found
42
- end
43
-
44
- # Wraps the given `block` with a `lambda`, rescue any error raised from the `block`.
45
- # Otherwise, error will be re-raised.
46
- #
47
- # @param action the `action` to be passed on to `rescue_actor`
48
- # @param format the `format` to be passed on to `rescue_actor`
49
- # @param context the context for the block evaluation
50
- # @param block the `block` to be evaluated
51
- #
52
- # @example Dispatch a cue that prints out a text
53
- # dispatch = self.class.dispatch_act(action: :create, format: :json, context: self) do
54
- # puts "completed the dispatch"
55
- # end
56
- #
57
- # dispatch.call == "completed the dispatch" # true
58
- #
59
- def dispatch_act(action: nil, format: nil, context: self, &block)
60
- raise ArgumentError, "block must be provided" unless block_given?
61
-
62
- lambda do
63
- context.instance_exec(&block)
64
- rescue StandardError => e
65
- (respond_to?(:rescue_actor) && rescue_actor(e, action:, format:, context:)) || raise
66
- end
67
- end
68
-
69
- # TODO: remove on next breaking change release
70
- alias dispatch_cue dispatch_act
71
- end
72
-
73
29
  # Calls the `actor` and passing arguments to it.
74
30
  # If a block is given, the result from the `actor` method will be yieled to the block.
75
31
  #
@@ -78,13 +34,13 @@ module MimeActor
78
34
  # @param actor either a method name or a Proc to evaluate
79
35
  # @param args arguments to be passed when calling the actor
80
36
  #
81
- def cue_actor(actor, *args, format:)
37
+ def cue_actor(actor, *args, action:, format:)
82
38
  dispatcher = MimeActor::Dispatcher.build(actor, *args)
83
39
  raise TypeError, "invalid actor, got: #{actor.inspect}" unless dispatcher
84
40
 
85
41
  self.class.validate!(:format, format)
86
42
 
87
- run_act_callbacks(format) do
43
+ run_act_callbacks(action:, format:) do
88
44
  result = dispatcher.call(self)
89
45
  block_given? ? yield(result) : result
90
46
  end
@@ -92,7 +48,7 @@ module MimeActor
92
48
  logger.error { "actor error, cause: #{e.inspect}" } unless raise_on_actor_error
93
49
  raise e if raise_on_actor_error
94
50
  rescue StandardError => e
95
- rescued = rescue_actor(e, action: action_name.to_sym, format: format)
51
+ rescued = rescue_actor(e, action:, format:)
96
52
  rescued || raise
97
53
  end
98
54
  end
@@ -49,6 +49,8 @@ module MimeActor
49
49
  #
50
50
  # @param unchecked the `actions` to be validated
51
51
  def validate_actions(unchecked)
52
+ return TypeError.new("actions must not be empty") if unchecked.empty?
53
+
52
54
  rejected = unchecked.reject { |action| action.is_a?(Symbol) }
53
55
  NameError.new("invalid actions, got: #{rejected.map(&:inspect).join(", ")}") if rejected.size.positive?
54
56
  end
@@ -73,6 +75,8 @@ module MimeActor
73
75
  #
74
76
  # @param unchecked the `formats` to be validated
75
77
  def validate_formats(unchecked)
78
+ return TypeError.new("formats must not be empty") if unchecked.empty?
79
+
76
80
  unfiltered = unchecked.to_set
77
81
  filtered = unfiltered & scene_formats
78
82
  rejected = unfiltered - filtered
@@ -13,8 +13,8 @@ module MimeActor
13
13
 
14
14
  module VERSION
15
15
  MAJOR = 0
16
- MINOR = 6
17
- BUILD = 4
16
+ MINOR = 7
17
+ BUILD = 1
18
18
  PRE = nil
19
19
 
20
20
  STRING = [MAJOR, MINOR, BUILD, PRE].compact.join(".")
metadata CHANGED
@@ -1,57 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mime_actor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.4
4
+ version: 0.7.1
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-07-26 00:00:00.000000000 Z
11
+ date: 2024-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: '7.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '5.0'
26
+ version: '7.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activesupport
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '5.0'
33
+ version: '7.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '5.0'
41
- - !ruby/object:Gem::Dependency
42
- name: rake
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '11.0'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '11.0'
40
+ version: '7.0'
55
41
  description: ''
56
42
  email:
57
43
  - ryancyq@gmail.com
@@ -59,6 +45,7 @@ executables: []
59
45
  extensions: []
60
46
  extra_rdoc_files: []
61
47
  files:
48
+ - CHANGELOG.md
62
49
  - COMPARE.md
63
50
  - LICENSE
64
51
  - README.md
@@ -91,9 +78,9 @@ require_paths:
91
78
  - lib
92
79
  required_ruby_version: !ruby/object:Gem::Requirement
93
80
  requirements:
94
- - - ">="
81
+ - - "~>"
95
82
  - !ruby/object:Gem::Version
96
- version: 3.1.0
83
+ version: '3.1'
97
84
  required_rubygems_version: !ruby/object:Gem::Requirement
98
85
  requirements:
99
86
  - - ">="