mime_actor 0.6.4 → 0.7.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: 17ce76fbcc753aab795a2b33622ab8be04b7c0249002691d83e3d8fb258d0ef8
4
- data.tar.gz: 1718a29351c4e71a4c5564aa6ddcfde879841464aa9a33c5867ffe590a259ecf
3
+ metadata.gz: c90519d1f76ba2e87980b02d88bffdf6dfe0ae9bb133df9bda76d52d2b98e2e3
4
+ data.tar.gz: fb54fdf81e39bcaa79ca0099ca8b8ecbfef7411558ae3e3b0e359c902e8dce96
5
5
  SHA512:
6
- metadata.gz: b6b6e35ef6c1cf0a099fb6a186814493d8596ffd8164875545047621400d85e150e33017dce7998089f6da90330f2a60525a6e73178feb756eb94cd06a4c3cc2
7
- data.tar.gz: a975ce751e17abf99b3a542ded6a029e987ee48133e3ef8a62cca03f65566724fb6cd6625983d63d7ece2604107d80479643c0ffc4ebbf535980914f5f65ad75
6
+ metadata.gz: 932b2de2863e2883093320d99a1c5e9b6545c1bb8c9d8c7f18e909b5608eee0acdca9286ccc594800dde39353fa5b5d92e7f1e6a95f07bc2dcb608eb60ceb695
7
+ data.tar.gz: f393a918ebac5edcae4d0de280f32e40152fedf1dcf2f717217387993f5d491bb64384333022a851dccf33b1786741bae19253cbf5d788d48bc23e189888b19d
data/COMPARE.md CHANGED
@@ -56,18 +56,18 @@ end
56
56
  #### before
57
57
  ```rb
58
58
  class EventsController < ActionController::Base
59
- def index
60
- @events = Event.all
61
- respond_to do |format|
62
- format.html do
63
- @event_categories = EventCategory.all
64
-
65
- # render html using @events and @event_categories
66
- render :index
67
- end
68
- format.json { render json: @events } # render json using #as_json
69
- end
59
+ def index
60
+ @events = Event.all
61
+ respond_to do |format|
62
+ format.html do
63
+ @event_categories = EventCategory.all
64
+
65
+ # render html using @events and @event_categories
66
+ render :index
67
+ end
68
+ format.json { render json: @events } # render json using #as_json
70
69
  end
70
+ end
71
71
  end
72
72
  ```
73
73
  #### after
@@ -75,21 +75,21 @@ end
75
75
  class EventsController < ActionController::Base
76
76
  include MimeActor::Action
77
77
 
78
- before_act -> { @events = Event.all }, action: :index
78
+ act_before -> { @events = Event.all }, action: :index
79
79
 
80
80
  # dynamically defines the action method according to on: argument
81
- respond_act_to :html, :json, on: :index
81
+ act_on_action :index, format: %i[html json]
82
82
 
83
- def index_html
84
- @event_categories = EventCategory.all
85
-
86
- # render html using @events and @event_categories
87
- render :index
88
- end
83
+ act_after :render_index_html, action: :index, format: :html
84
+ act_after -> { render json: @events }, action: :index, format: :json
89
85
 
90
- def index_json
91
- # render json using #as_json
92
- render json: @events
86
+ def index
87
+ @event_categories = EventCategory.all
88
+ end
89
+
90
+ def render_index_html
91
+ # render html using @events and @event_categories
92
+ render :index
93
93
  end
94
94
  end
95
95
  ```
@@ -145,28 +145,36 @@ end
145
145
  class EventsController < ActionController::Base
146
146
  include MimeActor::Action
147
147
 
148
- before_act :load_event, action: %i[show update]
148
+ act_before :load_event, action: %i[show update]
149
149
 
150
- respond_act_to :html, on: %i[show update]
151
- respond_act_to :json, on: %i[show update], with: -> { render json: @event } # render json using #as_json
150
+ act_on_action :show, :update, format: :html
151
+ act_on_action :show, :update, format: :json, with: -> { render json: @event } # render json using #as_json
152
152
 
153
- rescue_act_from ActiveRecord::RecordNotFound, format: :json, with: :handle_json_error
153
+ act_after :render_show_html, action: :show, format: :html
154
+ act_after :redirect_to_show, action: :update, format: :html
154
155
 
156
+ rescue_act_from ActiveRecord::RecordNotFound, format: :json, with: :handle_json_error
155
157
  rescue_act_from ActiveRecord::RecordNotFound, format: :html, action: :show do
156
158
  redirect_to events_path
157
159
  end
160
+ rescue_act_from ActiveRecord::RecrodNotSaved, format: :html, action: :update, with: -> { render :edit }
161
+
162
+ def show
163
+ # ...
164
+ end
165
+
166
+ def update
167
+ # ...
168
+ end
158
169
 
159
170
  private
160
-
161
- def show_html
171
+
172
+ def render_show_html
162
173
  render :show # render html using @event
163
174
  end
164
175
 
165
- def update_html
166
- # ...
176
+ def redirect_to_show
167
177
  redirect_to event_path(@event.id) # redirect to show upon sucessful update
168
- rescue ActiveRecord::RecordNotFound
169
- render :edit
170
178
  end
171
179
 
172
180
  def handle_json_error(error)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Mime Actor
1
+ # mime_actor
2
2
 
3
3
  [![Version][rubygems_badge]][rubygems]
4
4
  [![CI][ci_badge]][ci_workflows]
@@ -29,47 +29,151 @@ However, it can be a litte bit messy when some actions render more than a single
29
29
  ## Usage
30
30
 
31
31
  **Mime Actor** allows you to do something like:
32
+
33
+ #### Customize Action for certain MIME type
32
34
  ```rb
33
- class EventsController < ActionController::Base
34
- include MimeActor::Action
35
+ act_on_action :index, :show, format: %i[html json]
36
+ act_on_action :create, format: :json
37
+
38
+ def index
39
+ @events = Event.all
40
+ end
41
+ def show
42
+ @event = Event.find(params[:id])
43
+ end
44
+ def create
45
+ @event = Event.new.save!
46
+ end
47
+ ```
48
+ The above is equivalent to the following:
49
+ ```rb
50
+ def index
51
+ respond_to do |format|
52
+ format.html { @events = Event.all }
53
+ format.json { @events = Event.all }
54
+ end
55
+ end
56
+ def show
57
+ respond_to do |format|
58
+ format.html { @event = Event.find(params[:id]) }
59
+ format.json { @event = Event.find(params[:id]) }
60
+ end
61
+ end
62
+ def create
63
+ respond_to do |format|
64
+ format.json { @event = Event.new.save! }
65
+ end
66
+ end
67
+ ```
35
68
 
36
- before_act -> { @events = Event.all }, action: :index
37
- before_act :load_event, action: %i[show update]
38
- before_act -> { @event_categories = EventCategory.all }, action: :show, format: :html
69
+ #### Callback registration for certain Action + MIME type
70
+ ```rb
71
+ act_before :load_event_categories, action: :index, format: :html
39
72
 
40
- respond_act_to :html, :json, on: :update
41
- respond_act_to :html, on: %i[index show], with: :render_html
42
- respond_act_to :json, on: %i[index show], with: -> { render json: { action: action_name } }
73
+ act_on_action :index, :show, format: %i[html json]
43
74
 
44
- rescue_act_from ActiveRecord::RecordNotFound, format: :json, with: :handle_json_error
45
- rescue_act_from ActiveRecord::RecordNotFound, format: :html, action: :show, with: -> { redirect_to "/events" }
75
+ def index
76
+ @events = Event.all
77
+ end
46
78
 
47
- private
79
+ def show
80
+ @event = Event.find(params[:id])
81
+ end
48
82
 
49
- def update_html
50
- # ...
51
- redirect_to "/events/#{@event.id}" # redirect to show upon sucessful update
52
- rescue ActiveRecord::RecordNotFound
53
- render html: :edit
83
+ def load_event_categories
84
+ @event_categories = EventCategory.all
85
+ end
86
+ ```
87
+ The above is equivalent to the following:
88
+ ```rb
89
+ def index
90
+ respond_to do |format|
91
+ format.html do
92
+ load_event_categories
93
+ @events = Event.all
94
+ end
95
+ format.json { @events = Event.all }
96
+ end
54
97
  end
55
98
 
56
- def update_json
99
+ def show
100
+ respond_to do |format|
101
+ format.html { @event = Event.find(params[:id]) }
102
+ format.json { @event = Event.find(params[:id]) }
103
+ end
104
+ end
105
+
106
+ def load_event_categories
107
+ @event_categories = EventCategory.all
108
+ end
109
+ ```
110
+
111
+ #### Rescue handler registration for certain Action + MIME type
112
+ ```rb
113
+ act_before :load_event, action: %i[show update]
114
+
115
+ act_on_action :show, :update, format: :json
116
+ act_on_action :show, format: :html
117
+
118
+ rescue_act_from ActiveRecord::RecordNotFound, ActiveRecord::RecordNotSaved, format: :json, with: :handle_json_error
119
+ rescue_act_from ActiveRecord::RecordNotSaved, action: :show, format: :html, with: -> { redirect_to "/events" }
120
+ rescue_act_from ActiveRecord::RecordNotSaved, action: :update, format: :html, with: -> { render :edit }
121
+
122
+ def show
57
123
  # ...
58
- render json: @event # render with #to_json
59
124
  end
60
125
 
61
- def render_html
62
- render html: action_name
126
+ def update
127
+ @event.name = params[:name]
128
+ @event.save!
129
+ redirect_to "/events/#{@event.id}"
63
130
  end
64
131
 
65
132
  def load_event
66
- @event = Event.find(params.require(:event_id))
133
+ @event = Event.find(params[:id])
134
+ end
135
+
136
+ def handle_json_error(error)
137
+ render status: :bad_request, json: { error: error.message }
138
+ end
139
+ ```
140
+ The above is equivalent to the following:
141
+ ```rb
142
+ before_action :load_event, only: %i[show update]
143
+
144
+ rescue_from ActiveRecord::RecordNotFound, with: :handle_not_found_error
145
+ rescue_from ActiveRecord::RecordNotSaved, with: :handle_not_saved_error
146
+
147
+ def show
148
+ respond_to do |format|
149
+ format.html { ... }
150
+ format.json { ... }
151
+ end
152
+ end
153
+
154
+ def update
155
+ @event.name = params[:name]
156
+ @event.save!
157
+ redirect_to "/events/#{@event.id}"
158
+ end
159
+
160
+ def handle_not_found_error(error)
161
+ respond_to do |format|
162
+ format.html { redirect_to "/events" }
163
+ format.json { handle_json_error(error) }
164
+ end
165
+ end
166
+
167
+ def handle_not_saved_error(error)
168
+ respond_to do |format|
169
+ format.html { render :edit }
170
+ format.json { handle_json_error(error) }
171
+ end
67
172
  end
68
173
 
69
174
  def handle_json_error(error)
70
175
  render status: :bad_request, json: { error: error.message }
71
176
  end
72
- end
73
177
  ```
74
178
 
75
179
  ## Features
@@ -87,7 +191,8 @@ You can find the documentation on [RubyDoc][doc_mime_actor].
87
191
  ## Requirements
88
192
 
89
193
  - Ruby: MRI 3.1+
90
- - Rails: 5.0+
194
+ - ActiveSupport: 7.0+
195
+ - ActionPack: 7.0+
91
196
 
92
197
  ## Installation
93
198
 
@@ -7,8 +7,6 @@ require "mime_actor/scene"
7
7
  require "mime_actor/stage"
8
8
 
9
9
  require "active_support/concern"
10
- require "active_support/core_ext/module/attribute_accessors"
11
- require "active_support/core_ext/object/blank"
12
10
  require "active_support/lazy_load_hooks"
13
11
  require "abstract_controller/rendering"
14
12
  require "action_controller/metal/mime_responds"
@@ -30,37 +28,46 @@ module MimeActor
30
28
  include Stage
31
29
  include Logging
32
30
 
33
- included do
34
- mattr_accessor :actor_delegator, instance_writer: false, default: ->(action, format) { "#{action}_#{format}" }
35
- end
36
-
37
31
  # The core logic where rendering logics are collected as `Proc` and passed over to `ActionController::MimeResponds`
38
32
  #
33
+ # @param block the block to be evaluated
34
+ #
39
35
  # @example process `create` action
40
36
  # # it uses AbstractController#action_name to process
41
- # start_scene
37
+ # start_scene { create_internal }
42
38
  #
43
39
  # # it is equivalent to the following if `create` action is configured with `html` and `json` formats
44
40
  # def create
45
41
  # respond_to |format|
46
- # format.html { public_send(:create_html) }
47
- # format.json { public_send(:create_json) }
42
+ # format.html { create_internal }
43
+ # format.json { create_internal }
48
44
  # end
49
45
  # end
50
46
  #
51
- def start_scene
47
+ def start_scene(&block)
52
48
  action = action_name.to_sym
53
49
  formats = acting_scenes.fetch(action, {})
54
50
 
55
51
  if formats.empty?
56
- logger.warn { "format is empty for action: #{action_name.inspect}" }
57
- return
52
+ logger.warn { "no format found for action: #{action_name.inspect}" }
53
+ yield if block_given?
54
+ else
55
+ respond_to_scene(action, formats, block)
58
56
  end
57
+ end
58
+
59
+ private
59
60
 
61
+ def respond_to_scene(action, formats, block)
60
62
  respond_to do |collector|
61
63
  formats.each do |format, actor|
62
- callable = actor.presence || actor_delegator.call(action, format)
63
- dispatch = -> { cue_actor(callable, format:) }
64
+ callable = actor.presence || block
65
+ if callable.nil?
66
+ logger.warn { "no #respond_to handler found for action: #{action.inspect} format: #{format.inspect}" }
67
+ next
68
+ end
69
+
70
+ dispatch = -> { cue_actor(callable, action, format, action:, format:) }
64
71
  collector.public_send(format, &dispatch)
65
72
  end
66
73
  end
@@ -5,18 +5,24 @@
5
5
  require "mime_actor/validator"
6
6
 
7
7
  require "active_support/callbacks"
8
+ require "active_support/code_generator"
8
9
  require "active_support/concern"
9
- require "active_support/core_ext/array/wrap"
10
- require "active_support/core_ext/object/blank"
10
+ require "active_support/core_ext/module/attr_internal"
11
11
 
12
12
  module MimeActor
13
13
  # # MimeActor Callbacks
14
14
  #
15
15
  # MimeActor provides hooks during the life cycle of an act. Available callbacks are:
16
16
  #
17
- # - before_act
18
- # - around_act
19
- # - after_act
17
+ # - append_act_before
18
+ # - append_act_around
19
+ # - append_act_after
20
+ # - act_before
21
+ # - act_around
22
+ # - act_after
23
+ # - prepend_act_before
24
+ # - prepend_act_around
25
+ # - prepend_act_after
20
26
  #
21
27
  # NOTE: Calling the same callback multiple times will overwrite previous callback definitions.
22
28
  #
@@ -27,19 +33,25 @@ module MimeActor
27
33
  include MimeActor::Validator
28
34
 
29
35
  included do
36
+ attr_internal_reader :act_action, :act_format
30
37
  define_callbacks :act, skip_after_callbacks_if_terminated: true
31
-
32
- %i[before after around].each { |kind| define_act_callbacks(kind) }
38
+ generate_act_callback_methods
33
39
  end
34
40
 
35
41
  module ClassMethods
36
- class ActionMatcher
37
- def initialize(actions)
38
- @actions = Array.wrap(actions).to_set(&:to_s)
42
+ class ActMatcher
43
+ attr_reader :actions, :formats
44
+
45
+ def initialize(actions, formats)
46
+ @actions = actions&.then { |a| Array(a).to_set(&:to_sym) }
47
+ @formats = formats&.then { |f| Array(f).to_set(&:to_sym) }
39
48
  end
40
49
 
41
50
  def match?(controller)
42
- @actions.include?(controller.action_name)
51
+ matched = true
52
+ matched &= actions.include?(controller.act_action) if !actions.nil? && controller.respond_to?(:act_action)
53
+ matched &= formats.include?(controller.act_format) if !formats.nil? && controller.respond_to?(:act_format)
54
+ matched
43
55
  end
44
56
 
45
57
  alias after match?
@@ -47,154 +59,123 @@ module MimeActor
47
59
  alias around match?
48
60
  end
49
61
 
50
- def callback_chain_name(format = nil)
51
- if format.present?
52
- validate!(:format, format)
53
- :"act_#{format}"
54
- else
55
- :act
56
- end
57
- end
58
-
59
- def callback_chain_defined?(name)
60
- !!get_callbacks(name)
61
- end
62
-
63
62
  private
64
63
 
65
- def define_callback_chain(name)
66
- return if callback_chain_defined?(name)
67
-
68
- define_callbacks name, skip_after_callbacks_if_terminated: true
69
- end
70
-
71
64
  def configure_callbacks(callbacks, actions, formats, block)
72
65
  options = {}
73
- options[:if] = ActionMatcher.new(actions) if actions.present?
66
+ options[:if] = ActMatcher.new(actions, formats) unless actions.nil? && formats.nil?
74
67
  callbacks.push(block) if block
75
68
 
76
- formats = Array.wrap(formats)
77
- formats << nil if formats.empty?
78
-
79
69
  callbacks.each do |callback|
80
- formats.each do |format|
81
- chain = callback_chain_name(format)
82
- define_callback_chain(chain)
83
- yield chain, callback, options
84
- end
70
+ yield callback, options
85
71
  end
86
72
  end
87
73
 
88
74
  def validate_callback_options!(action, format)
89
- validate!(:action_or_actions, action) if action.present?
90
- validate!(:format_or_formats, format) if format.present?
75
+ validate!(:action_or_actions, action) unless action.nil?
76
+ validate!(:format_or_formats, format) unless format.nil?
91
77
  end
92
78
 
93
- def define_act_callbacks(kind)
94
- module_eval(
95
- # def self.before_act(*callbacks, action: nil, format: nil, &block)
96
- # validate_callback_options!(action, format)
97
- # configure_callbacks(callbacks, action, format, block) do |chain, callback, options|
98
- # set_callback(chain, :before, callback, options)
99
- # end
100
- # end
101
- #
102
- # def self.prepend_before_act(*callbacks, action: nil, format: nil, &block)
103
- # validate_callback_options!(action, format)
104
- # configure_callbacks(callbacks, action, format, block) do |chain, callback, options|
105
- # set_callback(chain, :before, callback, options.merge!(prepend: true))
106
- # end
107
- # end
108
- <<-RUBY, __FILE__, __LINE__ + 1
109
- def self.#{kind}_act(*callbacks, action: nil, format: nil, &block)
110
- validate_callback_options!(action, format)
111
- configure_callbacks(callbacks, action, format, block) do |chain, callback, options|
112
- set_callback(chain, :#{kind}, callback, options)
113
- end
114
- end
115
-
116
- def self.prepend_#{kind}_act(*callbacks, action: nil, format: nil, &block)
117
- validate_callback_options!(action, format)
118
- configure_callbacks(callbacks, action, format, block) do |chain, callback, options|
119
- set_callback(chain, :#{kind}, callback, options.merge!(prepend: true))
120
- end
121
- end
122
- RUBY
123
- )
79
+ def generate_act_callback_methods
80
+ ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner|
81
+ %i[before after around].each do |kind|
82
+ generate_act_callback_kind(owner, kind)
83
+ end
84
+ end
85
+ # as: check against the defined method in owner, code only generated after #batch block is yielded
86
+ ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner|
87
+ %i[before after around].each do |kind|
88
+ owner.define_cached_method(:"act_#{kind}", as: :"append_act_#{kind}", namespace: :mime_callbacks)
89
+ end
90
+ end
91
+ end
92
+
93
+ def generate_act_callback_kind(generator, kind)
94
+ generator.define_cached_method(:"append_act_#{kind}", namespace: :mime_callbacks) do |batch|
95
+ batch.push(
96
+ "def append_act_#{kind}(*callbacks, action: nil, format: nil, &block)",
97
+ "validate_callback_options!(action, format)",
98
+ "configure_callbacks(callbacks, action, format, block) do |callback, options|",
99
+ "set_callback(:act, :#{kind}, callback, options)",
100
+ "end",
101
+ "end"
102
+ )
103
+ end
104
+ generator.define_cached_method(:"prepend_act_#{kind}", namespace: :mime_callbacks) do |batch|
105
+ batch.push(
106
+ "def prepend_act_#{kind}(*callbacks, action: nil, format: nil, &block)",
107
+ "validate_callback_options!(action, format)",
108
+ "configure_callbacks(callbacks, action, format, block) do |callback, options|",
109
+ "set_callback(:act, :#{kind}, callback, options.merge!(prepend: true))",
110
+ "end",
111
+ "end"
112
+ )
113
+ end
124
114
  end
125
115
  end
126
116
 
127
117
  # Callbacks invocation sequence depends on the order of callback definition.
128
- # (except for callbacks with `format` filter).
129
118
  #
130
119
  # @example callbacks with/without action filter
131
- # before_act :my_before_act_one
132
- # before_act :my_before_act_two, action: :create
133
- # before_act :my_before_act_three
120
+ # act_before :my_act_before_one
121
+ # act_before :my_act_before_two, action: :create
122
+ # act_before :my_act_before_three
134
123
  #
135
- # around_act :my_around_act_one
136
- # around_act :my_around_act_two, action: :create
137
- # around_act :my_around_act_three
124
+ # act_around :my_act_around_one
125
+ # act_around :my_act_around_two, action: :create
126
+ # act_around :my_act_around_three
138
127
  #
139
- # after_act :my_after_act_one
140
- # after_act :my_after_act_two, action: :create
141
- # after_act :my_after_act_three
128
+ # act_after :my_act_after_one
129
+ # act_after :my_act_after_two, action: :create
130
+ # act_after :my_act_after_three
142
131
  #
143
132
  # # actual sequence:
144
- # # - my_before_act_one
145
- # # - my_before_act_two
146
- # # - my_before_act_three
147
- # # - my_around_act_one
148
- # # - my_around_act_two
149
- # # - my_around_act_three
150
- # # - my_after_act_three
151
- # # - my_after_act_two
152
- # # - my_after_act_one
133
+ # # - my_act_before_one
134
+ # # - my_act_before_two
135
+ # # - my_act_before_three
136
+ # # - my_act_around_one
137
+ # # - my_act_around_two
138
+ # # - my_act_around_three
139
+ # # - my_act_after_three
140
+ # # - my_act_after_two
141
+ # # - my_act_after_one
153
142
  #
154
143
  # @example callbacks with format filter
155
- # before_act :my_before_act_one
156
- # before_act :my_before_act_two, action: :create
157
- # before_act :my_before_act_three, action: :create, format: :html
158
- # before_act :my_before_act_four
144
+ # act_before :my_act_before_one
145
+ # act_before :my_act_before_two, action: :create
146
+ # act_before :my_act_before_three, action: :create, format: :html
147
+ # act_before :my_act_before_four
159
148
  #
160
- # around_act :my_around_act_one
161
- # around_act :my_around_act_two, action: :create, format: :html
162
- # around_act :my_around_act_three, action: :create
163
- # around_act :my_around_act_four
149
+ # act_around :my_act_around_one
150
+ # act_around :my_act_around_two, action: :create, format: :html
151
+ # act_around :my_act_around_three, action: :create
152
+ # act_around :my_act_around_four
164
153
  #
165
- # after_act :my_after_act_one, format: :html
166
- # after_act :my_after_act_two
167
- # after_act :my_after_act_three, action: :create
168
- # after_act :my_after_act_four
154
+ # act_after :my_act_after_one, format: :html
155
+ # act_after :my_act_after_two
156
+ # act_after :my_act_after_three, action: :create
157
+ # act_after :my_act_after_four
169
158
  #
170
159
  # # actual sequence:
171
- # # - my_before_act_one
172
- # # - my_before_act_two
173
- # # - my_before_act_four
174
- # # - my_around_act_one
175
- # # - my_around_act_three
176
- # # - my_around_act_four
177
- # # - my_before_act_three
178
- # # - my_around_act_two
179
- # # - my_after_act_one
180
- # # - my_after_act_four
181
- # # - my_after_act_three
182
- # # - my_after_act_two
160
+ # # - my_act_before_one
161
+ # # - my_act_before_two
162
+ # # - my_act_before_four
163
+ # # - my_act_before_three
164
+ # # - my_act_around_one
165
+ # # - my_act_around_two
166
+ # # - my_act_around_three
167
+ # # - my_act_around_four
168
+ # # - my_act_after_four
169
+ # # - my_act_after_three
170
+ # # - my_act_after_two
171
+ # # - my_act_after_one
183
172
  #
184
- def run_act_callbacks(format)
185
- action_chain = self.class.callback_chain_name
186
- format_chain = self.class.callback_chain_name(format)
187
-
188
- if self.class.callback_chain_defined?(format_chain)
189
- run_callbacks action_chain do
190
- run_callbacks format_chain do
191
- yield if block_given?
192
- end
193
- end
194
- else
195
- run_callbacks action_chain do
196
- yield if block_given?
197
- end
173
+ def run_act_callbacks(action:, format:)
174
+ @_act_action = action.to_sym
175
+ @_act_format = format.to_sym
176
+
177
+ run_callbacks :act do
178
+ yield if block_given?
198
179
  end
199
180
  end
200
181
  end
@@ -6,16 +6,6 @@ require "active_support/deprecation"
6
6
 
7
7
  module MimeActor
8
8
  def self.deprecator
9
- @deprecator ||= ActiveSupport::Deprecation.new("0.7.0", "MimeActor")
9
+ @deprecator ||= ActiveSupport::Deprecation.new("0.8.0", "MimeActor")
10
10
  end
11
11
  end
12
-
13
- [
14
- [MimeActor::Rescue::ClassMethods, { rescue_actor: "use #rescue_actor instance method" }],
15
- [MimeActor::Scene::ClassMethods, { act_on_format: :respond_act_to }],
16
- [MimeActor::Stage::ClassMethods, { actor?: "no longer supported, use Object#respond_to?" }],
17
- [MimeActor::Stage::ClassMethods, { dispatch_cue: "no longer support anonymous proc with rescue" }],
18
- [MimeActor::Stage::ClassMethods, { dispatch_act: "no longer support anonymous proc with rescue" }]
19
- ].each do |klazz, *args|
20
- MimeActor.deprecator.deprecate_methods(klazz, *args)
21
- end
@@ -39,9 +39,9 @@ module MimeActor
39
39
  end
40
40
  end
41
41
 
42
- class ActionExisted < ActionError
42
+ class ActionNotImplemented < ActionError
43
43
  def generate_message
44
- "action #{action.inspect} already existed"
44
+ "action #{action.inspect} not implemented"
45
45
  end
46
46
  end
47
47
  end
@@ -2,9 +2,12 @@
2
2
 
3
3
  # :markup: markdown
4
4
 
5
+ # required by active_support/tagged_logging
6
+ require "active_support/version"
7
+ require "active_support/isolated_execution_state" if ActiveSupport::VERSION::MAJOR >= 7
8
+
5
9
  require "active_support/concern"
6
10
  require "active_support/configurable"
7
- require "active_support/isolated_execution_state" # required by active_support/logger
8
11
  require "active_support/logger"
9
12
  require "active_support/tagged_logging"
10
13
 
@@ -18,7 +21,14 @@ module MimeActor
18
21
  include ActiveSupport::Configurable
19
22
 
20
23
  included do
21
- config_accessor :logger, default: ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new($stdout))
24
+ default_logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new($stdout))
25
+ if ActiveSupport::VERSION::MAJOR >= 7
26
+ config_accessor :logger, default: default_logger
27
+ else
28
+ config_accessor :logger do
29
+ default_logger
30
+ end
31
+ end
22
32
  end
23
33
 
24
34
  private
@@ -5,7 +5,7 @@
5
5
  module MimeActor
6
6
  class Railtie < Rails::Railtie
7
7
  initializer "mime_actor.deprecator", before: :load_environment_config do |app|
8
- app.deprecators[:mime_actor] = MimeActor.deprecator
8
+ app.deprecators[:mime_actor] = MimeActor.deprecator if Rails::VERSION::MAJOR >= 7
9
9
  end
10
10
  end
11
11
  end
@@ -6,9 +6,7 @@ require "mime_actor/dispatcher"
6
6
  require "mime_actor/validator"
7
7
 
8
8
  require "active_support/concern"
9
- require "active_support/core_ext/array/wrap"
10
9
  require "active_support/core_ext/module/attribute_accessors"
11
- require "active_support/core_ext/object/blank"
12
10
  require "active_support/core_ext/string/inflections"
13
11
 
14
12
  module MimeActor
@@ -47,13 +45,13 @@ module MimeActor
47
45
  #
48
46
  def rescue_act_from(*klazzes, action: nil, format: nil, with: nil, &block)
49
47
  raise ArgumentError, "error filter is required" if klazzes.empty?
50
- raise ArgumentError, "provide either with: or a block" unless with.present? ^ block_given?
48
+ raise ArgumentError, "provide either with: or a block" unless !with.nil? ^ block_given?
51
49
 
52
- validate!(:callable, with) if with.present?
50
+ validate!(:callable, with) unless with.nil?
53
51
  with = block if block_given?
54
52
 
55
- validate!(:action_or_actions, action) if action.present?
56
- validate!(:format_or_formats, format) if format.present?
53
+ validate!(:action_or_actions, action) unless action.nil?
54
+ validate!(:format_or_formats, format) unless format.nil?
57
55
 
58
56
  klazzes.each do |klazz|
59
57
  validate!(:klazz, klazz)
@@ -63,72 +61,6 @@ module MimeActor
63
61
  actor_rescuers << [error, format, action, with]
64
62
  end
65
63
  end
66
-
67
- # Resolve the error provided with the registered handlers.
68
- #
69
- # The handled error will be returned to indicate successful handling.
70
- #
71
- # @param error the error instance to rescue
72
- # @param action the `action` filter
73
- # @param format the `format` filter
74
- # @param context the context to evaluate for rescue handler
75
- # @param visited the errors to skip after no rescue handler matched the filter
76
- #
77
- def rescue_actor(error, action: nil, format: nil, context: self, visited: [])
78
- return if visited.include?(error)
79
-
80
- visited << error
81
- if (rescuer = dispatch_rescuer(error, format:, action:, context:))
82
- rescuer.call(error, format, action)
83
- error
84
- elsif error&.cause
85
- rescue_actor(error.cause, format:, action:, context:, visited:)
86
- end
87
- end
88
-
89
- private
90
-
91
- def dispatch_rescuer(error, format:, action:, context:)
92
- case rescuer = find_rescuer(error, format:, action:)
93
- when Symbol
94
- rescuer_method = context.method(rescuer)
95
- lambda do |*args|
96
- passable_args = rescuer_method.arity.negative? ? args : args.take(rescuer_method.arity)
97
- rescuer_method.call(*passable_args)
98
- end
99
- when Proc
100
- lambda do |*args|
101
- passable_args = rescuer.arity.negative? ? args : args.take(rescuer.arity)
102
- context.instance_exec(*passable_args, &rescuer)
103
- end
104
- end
105
- end
106
-
107
- def find_rescuer(error, format:, action:)
108
- return unless error
109
-
110
- *_, rescuer = actor_rescuers.reverse_each.detect do |rescuee, format_filter, action_filter|
111
- next if action_filter.present? && !Array.wrap(action_filter).include?(action)
112
- next if format_filter.present? && !Array.wrap(format_filter).include?(format)
113
- next unless (klazz = constantize_rescuee(rescuee))
114
-
115
- error.is_a?(klazz)
116
- end
117
- rescuer
118
- end
119
-
120
- def constantize_rescuee(class_or_name)
121
- case class_or_name
122
- when String, Symbol
123
- begin
124
- const_get(class_or_name)
125
- rescue NameError
126
- class_or_name.safe_constantize
127
- end
128
- else
129
- class_or_name
130
- end
131
- end
132
64
  end
133
65
 
134
66
  # Resolve the error provided with the registered handlers.
@@ -159,8 +91,8 @@ module MimeActor
159
91
  return unless error
160
92
 
161
93
  *_, rescuer = actor_rescuers.reverse_each.detect do |rescuee, format_filter, action_filter|
162
- next if action_filter.present? && !Array.wrap(action_filter).include?(action)
163
- next if format_filter.present? && !Array.wrap(format_filter).include?(format)
94
+ next unless action_filter.nil? || Array(action_filter).include?(action)
95
+ next unless format_filter.nil? || Array(format_filter).include?(format)
164
96
  next unless (klazz = constantize_rescuee(rescuee))
165
97
 
166
98
  error.is_a?(klazz)
@@ -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,87 @@ 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
112
-
113
- define_scene(action) unless action_defined
108
+ def generated_action_methods
109
+ @generated_action_methods ||= Module.new.tap { |mod| prepend mod }
114
110
  end
115
111
 
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
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.push(
117
+ "def #{action}",
118
+ "if respond_to?(:start_scene)",
119
+ "start_scene { raise #{MimeActor::ActionNotImplemented}, :#{action} unless defined?(super); super } ",
120
+ "else",
121
+ "raise #{MimeActor::ActionNotImplemented}, :#{action} unless defined?(super)",
122
+ "super",
123
+ "end",
124
+ "end"
125
+ )
124
126
  end
125
- RUBY
126
- )
127
+ end
128
+ end
127
129
  end
128
130
  end
129
131
  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 = 0
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.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-07-26 00:00:00.000000000 Z
11
+ date: 2024-07-30 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
@@ -91,9 +77,9 @@ require_paths:
91
77
  - lib
92
78
  required_ruby_version: !ruby/object:Gem::Requirement
93
79
  requirements:
94
- - - ">="
80
+ - - "~>"
95
81
  - !ruby/object:Gem::Version
96
- version: 3.1.0
82
+ version: '3.1'
97
83
  required_rubygems_version: !ruby/object:Gem::Requirement
98
84
  requirements:
99
85
  - - ">="