meta-api 0.0.3 → 0.0.5

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: 9b4cc662a6a580592192bcc29cd4367249d1bbba4678aee0f95d75d9ff01c219
4
- data.tar.gz: 5bd5ed38531f286ba422a57b8da12aceb0c13a063be1d5c0895c5073abd5a868
3
+ metadata.gz: a3fc58de696caf4cf3abade846de9af53bb6491bda575b361461aa67f07d2fb9
4
+ data.tar.gz: 68d00c2d43086af73a10bbe6bfc103a5ee8611751b01ad2b9c811edf9197438a
5
5
  SHA512:
6
- metadata.gz: b54219704c980114719f6cd9d53d1d77036a194eddd991610b88fe36f52ecf9e04abccfbfaad0d9acd527ba8edc294b333ca403752ff6a0a0f753c30e93d22cd
7
- data.tar.gz: 564d6e0dfcae8a11aa59bc740e7fff4901326eff996171539156ae064ce205173c548f295e6d0d8cb085fd4de3b829c7141e5cff5aa343e658f8b2475b004d02
6
+ metadata.gz: 18cf46619c9f286fe01fbc3b941a6ca5a5e380740ad94e4d6827040cd9c6a014f6249eb13bc609a9f56fe2c15da6e044250f610b0eafdf6202aa9b6a16a0f15b
7
+ data.tar.gz: edc1532a86fe8776f061cfd1f550392ba280a4e693437b6d6260bfcfce8f0a33d7ae66e6f95250fb8530751307751a53857c0fd2a32a57ec9f8764bf6a9037da
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # 更新日志
2
2
 
3
+ ## 0.0.5(2023 年 4 月 18 日)
4
+
5
+ 1. 调整了 `around` 钩子的执行顺序,它与 `before` 钩子共同定义顺序。
6
+ 2. 修复了若干 bug.
7
+
8
+ ## 0.0.4(2023 年 4 月 18 日)
9
+
10
+ 1. `Application` 添加 `.around` 宏。
11
+ 2. `render` 时支持传递 `user_data` 选项,用作 value 解析的第二个参数。
12
+
3
13
  ## 0.0.3(2023 年 4 月 4 日)
4
14
 
5
15
  1. 添加两个新的选项 `ref:` 和 `dynamic_ref:`,以便后期取代 `using:`.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- meta-api (0.0.3)
4
+ meta-api (0.0.5)
5
5
 
6
6
  GEM
7
7
  remote: https://gems.ruby-china.com/
@@ -63,4 +63,4 @@ DEPENDENCIES
63
63
  rspec
64
64
 
65
65
  BUNDLED WITH
66
- 2.3.17
66
+ 2.3.7
data/README.md CHANGED
@@ -17,7 +17,7 @@ $ git clone https://github.com/yetrun/web-frame-example.git
17
17
  在 Gemfile 中添加:
18
18
 
19
19
  ```ruby
20
- gem 'meta-api', '~> 0.0.3' # Meta 框架处于快速开发阶段,引入时应尽量固定版本
20
+ gem 'meta-api', '~> 0.0.5' # Meta 框架处于快速开发阶段,引入时应尽量固定版本
21
21
  ```
22
22
 
23
23
  然后在 Ruby 代码中引用:
@@ -1,6 +1,14 @@
1
1
  zh-CN:
2
2
  json_schema:
3
+ type_names:
4
+ basic: "基本类型"
5
+ array: "数组类型"
3
6
  errors:
4
- required: '未提供'
5
- format: '格式不正确'
6
- allowable: '不在允许的值范围内'
7
+ required: "未提供"
8
+ format: "格式不正确"
9
+ allowable: "不在允许的值范围内"
10
+ type_convert:
11
+ basic: "类型转化失败,期望得到一个 `%{target_type}` 类型,但值 `%{value}` 无法转化"
12
+ array: "转化为数组类型时期望对象拥有 `to_a` 方法"
13
+ object: "类型转化失败,期望得到一个 `object` 类型,但值 `%{value}` 是一个%{real_type}"
14
+ unknown: "未知的目标类型 `%{target_type}`"
@@ -118,7 +118,7 @@ class DemoApp < Meta::Application
118
118
  end
119
119
  ```
120
120
 
121
- ### before/after 钩子
121
+ ### 钩子
122
122
 
123
123
  *(如果不涉及到钩子和异常拦截,嵌套路由将毫无意义。)*
124
124
 
@@ -142,6 +142,33 @@ class DemoApp < Meta::Application
142
142
  end
143
143
  ```
144
144
 
145
+ 同时还支持 `around` 钩子(*试验特性*):
146
+
147
+ ```ruby
148
+ class DemoApp < Meta::Application
149
+ before { puts 1 }
150
+ around { |next_action|
151
+ puts 2
152
+ next_action.execute(self)
153
+ puts 7
154
+ }
155
+ before { puts 3 }
156
+ after { puts 5 }
157
+ after { puts 6 }
158
+
159
+ get '/request' do
160
+ puts 4
161
+ end
162
+ end
163
+ ```
164
+
165
+ 所有钩子的执行顺序是:
166
+
167
+ 1. `before` 钩子和 `around` 钩子的前半部分,按照定义的顺序执行
168
+ 2. 然后执行路由方法。
169
+ 3. 然后执行 `after` 钩子,按照定义的顺序执行。
170
+ 4. 最后执行 `around` 钩子的后半部分,按照定义的逆序执行。
171
+
145
172
  ### 异常拦截
146
173
 
147
174
  在 `namespace` 中使用 `rescue_error` 拦截异常。
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /home/run/workspace/personal/web-frame
3
3
  specs:
4
- meta-api (0.0.2)
4
+ meta-api (0.0.4)
5
5
 
6
6
  GEM
7
7
  remote: https://gems.ruby-china.com/
@@ -123,7 +123,7 @@ module Meta
123
123
  if request_body.empty?
124
124
  json = {}
125
125
  elsif !request.content_type.start_with?('application/json')
126
- raise Errors::UnsupportedContentType, "只接受 Content-Type 为 application/json 的请求参数"
126
+ raise Errors::UnsupportedContentType, "只接受 Content-Type 为 application/json 的请求参数,当前格式:#{request.content_type}"
127
127
  else
128
128
  json = JSON.parse(request_body)
129
129
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # 洋葱圈模型的链式调用,需要结合 Meta::RouteDSL::AroundActionBuilder 才可以看到它奇效。
4
+
5
+ module Meta
6
+ module RouteDSL
7
+ class LinkedAction
8
+ def initialize(current_proc, next_action)
9
+ @current_proc = current_proc
10
+ @next_action = next_action
11
+ end
12
+
13
+ def execute(execution)
14
+ execution.instance_exec(@next_action, &@current_proc)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -51,7 +51,7 @@ module Meta
51
51
  }.compact
52
52
  end unless responses.empty?
53
53
 
54
- operation_object
54
+ operation_object.compact
55
55
  end
56
56
 
57
57
  def self.new(meta = {})
@@ -28,11 +28,11 @@ module Meta
28
28
  {
29
29
  name: name,
30
30
  in: options[:in],
31
- required: property_options[:required] || nil,
31
+ required: property_options.key?(:required) ? property_options[:required] : false,
32
32
  description: property_options[:description] || '',
33
33
  schema: {
34
34
  type: property_options[:type]
35
- }
35
+ }.compact
36
36
  }.compact
37
37
  end unless parameters.empty?
38
38
  end
@@ -8,24 +8,23 @@ module Meta
8
8
  class Route
9
9
  include PathMatchingMod.new(path_method: :path, matching_mode: :full)
10
10
 
11
- attr_reader :path, :method, :meta, :actions
11
+ attr_reader :path, :method, :meta, :action
12
12
 
13
- def initialize(path: '', method: :all, meta: {}, actions: [])
13
+ def initialize(path: '', method: :all, meta: {}, action: nil)
14
14
  @path = Utils::Path.normalize_path(path)
15
15
  @method = method
16
16
  @meta = Metadata.new(meta)
17
- @actions = actions
17
+ @action = action
18
18
  end
19
19
 
20
20
  def execute(execution, remaining_path)
21
21
  path_matching.merge_path_params(remaining_path, execution.request)
22
22
 
23
- # 依次执行这个环境
24
23
  begin
25
24
  execution.parse_parameters(@meta[:parameters]) if @meta[:parameters]
26
25
  execution.parse_request_body(@meta[:request_body]) if @meta[:request_body]
27
26
 
28
- actions.each { |b| execution.instance_eval(&b) }
27
+ action.execute(execution) if action
29
28
 
30
29
  render_entity(execution) if @meta[:responses]
31
30
  rescue Execution::Abort
@@ -5,15 +5,10 @@ module Meta
5
5
  class ArraySchemaBuilder
6
6
  def initialize(options, &block)
7
7
  options = options.dup
8
- if options[:items]
9
- items_options = options.delete(:items)
10
- elsif options[:ref]
11
- items_options = { ref: options.delete(:ref) }
12
- elsif options[:dynamic_ref]
13
- items_options = { dynamic_ref: options.delete(:dynamic_ref) }
14
- else
15
- items_options = {}
16
- end
8
+ items_options = options.delete(:items) || {}
9
+ items_options[:ref] = options.delete(:ref) if options[:ref]
10
+ items_options[:dynamic_ref] = options.delete(:dynamic_ref) if options[:dynamic_ref]
11
+
17
12
  @items_schema = SchemaBuilderTool.build(items_options, &block)
18
13
  @base_options = options
19
14
  end
@@ -6,6 +6,12 @@ require_relative '../support/schema_options'
6
6
  module Meta
7
7
  module JsonSchema
8
8
  class BaseSchema
9
+ OPTIONS_CHECKER = Utils::KeywordArgs::Builder.build do
10
+ key :type, :items, :description, :presenter, :value, :default, :properties, :convert
11
+ key :validate, :required, :format, :allowable
12
+ key :param, :render
13
+ end
14
+
9
15
  # `options` 包含了转换器、验证器、文档、选项。
10
16
  #
11
17
  # 由于本对象可继承,基于不同的继承可分别表示基本类型、对象和数组,所以该属
@@ -16,11 +22,12 @@ module Meta
16
22
  attr_reader :options
17
23
 
18
24
  def initialize(options = {})
25
+ options = OPTIONS_CHECKER.check(options)
19
26
  @options = SchemaOptions.normalize(options)
20
27
  end
21
28
 
22
29
  USER_OPTIONS_CHECKER = Utils::KeywordArgs::Builder.build do
23
- key :stage, :execution, :object_value, :type_conversion, :validation
30
+ key :stage, :execution, :object_value, :type_conversion, :validation, :user_data
24
31
 
25
32
  # 以下三个是 ObjectSchema 需要的选项
26
33
  key :discard_missing, :exclude, :scope
@@ -57,7 +64,13 @@ module Meta
57
64
 
58
65
  def resolve_value(user_options)
59
66
  value_proc = options[:value]
60
- value_proc_params = (value_proc.lambda? && value_proc.arity == 0) ? [] : [user_options[:object_value]]
67
+ if value_proc.lambda?
68
+ value_proc_params = []
69
+ value_proc_params << user_options[:object_value] if value_proc.arity >= 1
70
+ value_proc_params << user_options[:user_data] if value_proc.arity >= 2
71
+ else
72
+ value_proc_params = [user_options[:object_value], user_options[:user_data]]
73
+ end
61
74
 
62
75
  if user_options[:execution]
63
76
  user_options[:execution].instance_exec(*value_proc_params, &value_proc)
@@ -5,13 +5,6 @@ require_relative '../../utils/kwargs/builder'
5
5
  module Meta
6
6
  module JsonSchema
7
7
  module SchemaOptions
8
- OPTIONS_CHECKER = Utils::KeywordArgs::Builder.build do
9
- key :type, :items, :description, :presenter, :value, :format, :required, :default, :validate, :allowable, :properties, :convert
10
- key :using, normalizer: ->(value) { value.is_a?(Proc) ? { resolve: value } : value }
11
- key :param
12
- key :render
13
- end
14
-
15
8
  @default_options = {
16
9
  scope: [],
17
10
  required: false
@@ -34,8 +27,6 @@ module Meta
34
27
  end
35
28
 
36
29
  def normalize(options)
37
- options = OPTIONS_CHECKER.check(options)
38
-
39
30
  # 只要 options 中设置为 nil 的选项没有明确的意义,则下行代码是永远有效的
40
31
  options = (@default_options.compact).merge(options.compact)
41
32
  if options[:using]
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bigdecimal'
4
+
3
5
  module Meta
4
6
  module JsonSchema
5
7
  class ObjectWrapper
@@ -29,7 +31,7 @@ module Meta
29
31
  @definity_types = {
30
32
  'boolean' => [TrueClass, FalseClass],
31
33
  'integer' => [Integer],
32
- 'number' => [Integer, Float],
34
+ 'number' => [Integer, Float, BigDecimal],
33
35
  'string' => [String],
34
36
  'array' => [Array],
35
37
  'object' => [Hash, ObjectWrapper]
@@ -39,7 +41,7 @@ module Meta
39
41
  @boolean_converters = {
40
42
  [String] => lambda do |value|
41
43
  unless %w[true True TRUE false False FALSE].include?(value)
42
- raise TypeConvertError, "类型转化失败,期望得到一个 `boolean` 类型,但值 `#{value}` 无法转化"
44
+ raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.basic', target_type: 'boolean', value: value)
43
45
  end
44
46
 
45
47
  value.downcase == 'true'
@@ -50,14 +52,14 @@ module Meta
50
52
  [String] => lambda do |value|
51
53
  # 允许的格式:+34、-34、34、34.0 等
52
54
  unless value =~ /^[+-]?\d+(\.0+)?$/
53
- raise TypeConvertError, "类型转化失败,期望得到一个 `integer` 类型,但值 `#{value}` 无法转化"
55
+ raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.basic', target_type: 'integer', value: value)
54
56
  end
55
57
 
56
58
  value.to_i
57
59
  end,
58
- [Float] => lambda do |value|
60
+ [Float, BigDecimal] => lambda do |value|
59
61
  unless value.to_i == value
60
- raise TypeConvertError, "类型转化失败,期望得到一个 `integer` 类型,但值 `#{value}` 无法转化"
62
+ raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.basic', target_type: 'integer', value: value)
61
63
  end
62
64
 
63
65
  value.to_i
@@ -67,7 +69,7 @@ module Meta
67
69
  @number_converters = {
68
70
  [String] => lambda do |value|
69
71
  unless value =~ /^[+-]?\d+(\.\d+)?$/
70
- raise TypeConvertError, "类型转化失败,期望得到一个 `number` 类型,但值 `#{value}` 无法转化"
72
+ raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.basic', target_type: 'number', value: value)
71
73
  end
72
74
 
73
75
  float = value.to_f
@@ -84,7 +86,7 @@ module Meta
84
86
  @array_converters = {
85
87
  [Object] => lambda do |value|
86
88
  unless value.respond_to?(:to_a)
87
- raise TypeConvertError, "转化为数组类型时期望对象拥有 `to_a` 方法"
89
+ raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.array')
88
90
  end
89
91
 
90
92
  value.to_a
@@ -94,9 +96,9 @@ module Meta
94
96
  @object_converters = {
95
97
  [Object] => lambda do |value|
96
98
  if [TrueClass, FalseClass, Integer, Float, String].any? { |ruby_type| value.is_a?(ruby_type) }
97
- raise TypeConvertError, "类型转化失败,期望得到一个 `object` 类型,但值 `#{value}` 是一个基本类型"
99
+ raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.object', value: value, real_type: I18n.t(:'json_schema.type_description.basic'))
98
100
  elsif value.is_a?(Array)
99
- raise TypeConvertError, "类型转化失败,期望得到一个 `object` 类型,但值 `#{value}` 是一个 `array` 类型"
101
+ raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.object', value: value, real_type: I18n.t(:'json_schema.type_description.array'))
100
102
  end
101
103
 
102
104
  ObjectWrapper.new(value)
@@ -106,7 +108,7 @@ module Meta
106
108
  class << self
107
109
  def convert_value(value, target_type)
108
110
  return nil if value.nil?
109
- raise JsonSchema::TypeConvertError, "未知的目标类型 `#{target_type}`" unless @definity_types.keys.include?(target_type)
111
+ raise JsonSchema::TypeConvertError, I18n.t(:'json_schema.errors.type_convert.unknown') unless @definity_types.keys.include?(target_type)
110
112
  return value if match_definity_types?(value, target_type)
111
113
 
112
114
  convert_to_definity_type(value, target_type)
@@ -126,7 +128,7 @@ module Meta
126
128
  return converter.call(value)
127
129
  end
128
130
  end
129
- raise TypeConvertError, "类型转化失败,期望得到一个 `#{target_type}` 类型,但值 `#{value}` 无法转化"
131
+ raise TypeConvertError, I18n.t(:'json_schema.errors.type_convert.basic', target_type: target_type, value: value)
130
132
  end
131
133
  end
132
134
  end
@@ -10,8 +10,7 @@ module Meta
10
10
 
11
11
  def initialize(prefix = nil, &block)
12
12
  @mod_prefix = prefix
13
- @before_callbacks = []
14
- @after_callbacks = []
13
+ @callbacks = []
15
14
  @error_guards = []
16
15
  @meta_builder = MetaBuilder.new
17
16
  @mod_builders = []
@@ -20,7 +19,7 @@ module Meta
20
19
  instance_exec &block if block_given?
21
20
  end
22
21
 
23
- def build(parent_path: '', meta: {}, before_callbacks: [], after_callbacks: [])
22
+ def build(parent_path: '', meta: {}, callbacks: [])
24
23
  # 合并 meta 时不仅仅是覆盖,比如 parameters 参数需要合并
25
24
  meta2 = (meta || {}).merge(@meta_builder.build)
26
25
  if meta[:parameters] && meta2[:parameters]
@@ -28,9 +27,11 @@ module Meta
28
27
  end
29
28
 
30
29
  # 构建子模块
31
- before_callbacks += @before_callbacks
32
- after_callbacks = @after_callbacks + after_callbacks
33
- mods = @mod_builders.map { |builder| builder.build(parent_path: Utils::Path.join(parent_path, @mod_prefix), meta: meta2, before_callbacks: before_callbacks, after_callbacks: after_callbacks) }
30
+ # 合并父级传递过来的 callbacks,将 before 和 around 放在前面,after 放在后面
31
+ parent_before = callbacks.filter { |cb| cb[:lifecycle] == :before || cb[:lifecycle] == :around }
32
+ parent_after = callbacks.filter { |cb| cb[:lifecycle] == :after }
33
+ callbacks = parent_before + @callbacks + parent_after
34
+ mods = @mod_builders.map { |builder| builder.build(parent_path: Utils::Path.join(parent_path, @mod_prefix), meta: meta2, callbacks: callbacks) }
34
35
 
35
36
  Application.new(
36
37
  prefix: @mod_prefix,
@@ -64,11 +65,24 @@ module Meta
64
65
 
65
66
  # 定义模块内的公共逻辑
66
67
  def before(&block)
67
- @before_callbacks << block
68
+ @callbacks << {
69
+ lifecycle: :before,
70
+ proc: block
71
+ }
68
72
  end
69
73
 
70
74
  def after(&block)
71
- @after_callbacks << block
75
+ @callbacks << {
76
+ lifecycle: :after,
77
+ proc: block
78
+ }
79
+ end
80
+
81
+ def around(&block)
82
+ @callbacks << {
83
+ lifecycle: :around,
84
+ proc: block
85
+ }
72
86
  end
73
87
 
74
88
  def rescue_error(error_class, &block)
@@ -94,13 +108,13 @@ module Meta
94
108
  @meta = meta
95
109
  end
96
110
 
97
- def build(parent_path: '', meta: {}, before_callbacks: [], after_callbacks: [])
111
+ def build(parent_path: '', meta: {}, **kwargs)
98
112
  # 合并 meta 时不仅仅是覆盖,比如 parameters 参数需要合并
99
113
  meta2 = (meta || {}).merge(@meta)
100
114
  if meta[:parameters] && meta2[:parameters]
101
115
  meta2[:parameters] = meta[:parameters].merge(meta2[:parameters])
102
116
  end
103
- @builder.build(parent_path: parent_path, meta: meta2, before_callbacks: before_callbacks, after_callbacks: after_callbacks)
117
+ @builder.build(parent_path: parent_path, meta: meta2, **kwargs)
104
118
  end
105
119
  end
106
120
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # 洋葱圈模型的构建器。
4
+ #
5
+ # 因为应用的底层仅使用洋葱圈模型,所以在 DSL 层面,我们需要将 before、after、around 等序列和当前 action 共同构建洋葱圈模型。
6
+ # 在这个类中,仅仅列出了 before、around、after 三个方法。
7
+ # 因此第一步,当前执行的 action 会用 before 逻辑代替。
8
+ # 其次,有必要声明 before、after、around 的执行顺序。before 和 after 的顺序关系容易理解,重点是要关注 around 的执行顺序。
9
+ # around 的前半部分与 before 的关系:按照声明的顺序执行。
10
+ # around 与 after 的关系:after 序列会在之前执行,然后是 around 序列的后半部分。
11
+
12
+ require_relative '../application/linked_action'
13
+
14
+ module Meta
15
+ module RouteDSL
16
+ class AroundActionBuilder
17
+ def initialize
18
+ @before = []
19
+ @after = []
20
+ end
21
+
22
+ def around(&block)
23
+ @before << block
24
+ end
25
+
26
+ def before(&block)
27
+ @before << Proc.new do |next_action|
28
+ self.instance_exec(&block)
29
+ next_action.execute(self) if next_action
30
+ end
31
+ end
32
+
33
+ def after(&block)
34
+ # 在洋葱圈模型中,先声明的 after 逻辑会在最后执行,因此为了保证 after 逻辑的执行顺序
35
+ @after.unshift(Proc.new do |next_action|
36
+ next_action.execute(self) if next_action
37
+ self.instance_exec(&block)
38
+ end)
39
+ end
40
+
41
+ def build
42
+ # 从后向前构建
43
+ (@before + @after).reverse.reduce(nil) do |following, p|
44
+ LinkedAction.new(p, following)
45
+ end
46
+ end
47
+
48
+ # 使用 before、after、around 系列和当前 action 共同构建洋葱圈模型。
49
+ # Note: 该方法可能被废弃!
50
+ #
51
+ # 构建成功后,执行顺序是:
52
+ #
53
+ # - before 序列
54
+ # - around 序列的前半部分
55
+ # - action
56
+ # - around 序列的后半部分
57
+ # - after 序列
58
+ #
59
+ def self.build(before: [], after: [], around: [], action: nil)
60
+ builder = AroundActionBuilder.new
61
+
62
+ # 首先构建 before 序列,保证它最先执行
63
+ builder.around do |next_action|
64
+ before.each { |p| self.instance_exec(&p) }
65
+ next_action.execute(self)
66
+ end unless before.empty?
67
+ # 然后构建 after 序列,保证它最后执行
68
+ builder.around do |next_action|
69
+ next_action.execute(self)
70
+ after.each { |p| self.instance_exec(&p) }
71
+ end unless after.empty?
72
+ # 接着应用洋葱圈模型,依次构建 around 序列、action
73
+ around.each { |p| builder.around(&p) }
74
+ builder.around { self.instance_exec(&action) } unless action.nil?
75
+
76
+ builder.build
77
+ end
78
+ end
79
+ end
80
+ end
@@ -7,6 +7,7 @@ require_relative 'helpers'
7
7
  require_relative 'chain_builder'
8
8
  require_relative 'action_builder'
9
9
  require_relative 'meta_builder'
10
+ require_relative 'around_action_builder'
10
11
 
11
12
  module Meta
12
13
  module RouteDSL
@@ -24,7 +25,7 @@ module Meta
24
25
  instance_exec &block if block_given?
25
26
  end
26
27
 
27
- def build(parent_path: '', meta: {}, before_callbacks: [], after_callbacks: [])
28
+ def build(parent_path: '', meta: {}, callbacks: {})
28
29
  # 合并 meta 时不仅仅是覆盖,比如 parameters 参数需要合并
29
30
  meta2 = (meta || {}).merge(@meta_builder.build)
30
31
  if meta[:parameters] && meta2[:parameters]
@@ -45,15 +46,23 @@ module Meta
45
46
  end
46
47
  end
47
48
 
48
- # before_callbacks、action、after_callbacks 合并为 actions
49
- action = @action_builder&.build
50
- actions = before_callbacks + (action ? [action] : []) + after_callbacks
49
+ # 构建洋葱圈模型的 LinkedAction
50
+ # 合并父级传递过来的 callbacks,将 before 和 around 放在前面,after 放在后面
51
+ parent_before = callbacks.filter { |cb| cb[:lifecycle] == :before || cb[:lifecycle] == :around }
52
+ parent_after = callbacks.filter { |cb| cb[:lifecycle] == :after }
53
+ callbacks = parent_before + [{ lifecycle: :before, proc: @action_builder&.build }] + parent_after
54
+ # 使用 AroundActionBuilder 构建洋葱圈模型
55
+ around_action_builder = AroundActionBuilder.new
56
+ callbacks.each do |cb|
57
+ around_action_builder.send(cb[:lifecycle], &cb[:proc]) if cb[:proc]
58
+ end
59
+ action = around_action_builder.build
51
60
 
52
61
  Route.new(
53
62
  path: @path,
54
63
  method: @method,
55
64
  meta: meta2,
56
- actions: actions
65
+ action: action
57
66
  )
58
67
  end
59
68
 
data/meta-api.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "meta-api"
3
- spec.version = "0.0.3"
3
+ spec.version = "0.0.5"
4
4
  spec.authors = ["yetrun"]
5
5
  spec.email = ["yetrun@foxmail.com"]
6
6
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: meta-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - yetrun
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-04 00:00:00.000000000 Z
11
+ date: 2023-04-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: 一个 Web API 框架,该框架采用定义元信息的方式编写 API,并同步生成 API 文档
14
14
  email:
@@ -81,6 +81,7 @@ files:
81
81
  - lib/meta/application.rb
82
82
  - lib/meta/application/application.rb
83
83
  - lib/meta/application/execution.rb
84
+ - lib/meta/application/linked_action.rb
84
85
  - lib/meta/application/metadata.rb
85
86
  - lib/meta/application/parameters.rb
86
87
  - lib/meta/application/path_matching_mod.rb
@@ -110,6 +111,7 @@ files:
110
111
  - lib/meta/rails.rb
111
112
  - lib/meta/route_dsl/action_builder.rb
112
113
  - lib/meta/route_dsl/application_builder.rb
114
+ - lib/meta/route_dsl/around_action_builder.rb
113
115
  - lib/meta/route_dsl/chain_builder.rb
114
116
  - lib/meta/route_dsl/helpers.rb
115
117
  - lib/meta/route_dsl/meta_builder.rb