meta-api 0.0.3 → 0.0.4

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: bc6f6c3dddfd1e683cae7147be60bd7ead57e46b097700fc1db4113d12f08965
4
+ data.tar.gz: 476caee521969debd84614433ab41be89fbe7d1e5b074f46a6c8df40ca2d2c43
5
5
  SHA512:
6
- metadata.gz: b54219704c980114719f6cd9d53d1d77036a194eddd991610b88fe36f52ecf9e04abccfbfaad0d9acd527ba8edc294b333ca403752ff6a0a0f753c30e93d22cd
7
- data.tar.gz: 564d6e0dfcae8a11aa59bc740e7fff4901326eff996171539156ae064ce205173c548f295e6d0d8cb085fd4de3b829c7141e5cff5aa343e658f8b2475b004d02
6
+ metadata.gz: 3e213a983004b7fed1d15c2e0b41c9746584d259af6f969c631669b7efbb63f5573ee67b61ca5cb95888738925ca525e2ae4045502a7998c57c96432c12e3fd7
7
+ data.tar.gz: b575cf5145a3693ceb14296178c25c1ab1f8bf23e2a7dac92e9cc131a82dd79649d101f066c716069a5cb99114c138842b7bde89734790f217095fd459bf8fb4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # 更新日志
2
2
 
3
+ ## 0.0.4(2023 年 4 月 18 日)
4
+
5
+ 1. `Application` 添加 `.around` 宏。
6
+ 2. `render` 时支持传递 `user_data` 选项,用作 value 解析的第二个参数。
7
+
3
8
  ## 0.0.3(2023 年 4 月 4 日)
4
9
 
5
10
  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.4)
5
5
 
6
6
  GEM
7
7
  remote: https://gems.ruby-china.com/
@@ -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,34 @@ 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
+ before { puts 2 }
151
+ around { |next_action|
152
+ puts 3
153
+ next_action.execute(self)
154
+ puts 5
155
+ }
156
+ after { puts 6 }
157
+ after { puts 7 }
158
+
159
+ get '/request' do
160
+ puts 4
161
+ end
162
+ end
163
+ ```
164
+
165
+ 所有钩子的执行顺序是:
166
+
167
+ 1. 首先执行 `before` 钩子,按照定义的顺序执行。
168
+ 2. 然后执行 `around` 钩子的前半部分,按照定义的顺序执行。
169
+ 3. 然后执行路由方法。
170
+ 4. 然后执行 `around` 钩子的后半部分,按照定义的顺序执行。
171
+ 5. 最后执行 `after` 钩子,按照定义的顺序执行。
172
+
145
173
  ### 异常拦截
146
174
 
147
175
  在 `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.3)
5
5
 
6
6
  GEM
7
7
  remote: https://gems.ruby-china.com/
@@ -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
@@ -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]
@@ -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 = { before: [], after: [], around: [] }
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,12 @@ 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 = { # 合并父级传递过来的 callbacks
31
+ before: (callbacks[:before] || []) + @callbacks[:before],
32
+ around: (callbacks[:around] || []) + @callbacks[:around],
33
+ after: @callbacks[:after] + (callbacks[:after] || []),
34
+ }
35
+ mods = @mod_builders.map { |builder| builder.build(parent_path: Utils::Path.join(parent_path, @mod_prefix), meta: meta2, callbacks: callbacks) }
34
36
 
35
37
  Application.new(
36
38
  prefix: @mod_prefix,
@@ -64,11 +66,15 @@ module Meta
64
66
 
65
67
  # 定义模块内的公共逻辑
66
68
  def before(&block)
67
- @before_callbacks << block
69
+ @callbacks[:before] << block
68
70
  end
69
71
 
70
72
  def after(&block)
71
- @after_callbacks << block
73
+ @callbacks[:after] << block
74
+ end
75
+
76
+ def around(&block)
77
+ @callbacks[:around] << block
72
78
  end
73
79
 
74
80
  def rescue_error(error_class, &block)
@@ -94,13 +100,13 @@ module Meta
94
100
  @meta = meta
95
101
  end
96
102
 
97
- def build(parent_path: '', meta: {}, before_callbacks: [], after_callbacks: [])
103
+ def build(parent_path: '', meta: {}, **kwargs)
98
104
  # 合并 meta 时不仅仅是覆盖,比如 parameters 参数需要合并
99
105
  meta2 = (meta || {}).merge(@meta)
100
106
  if meta[:parameters] && meta2[:parameters]
101
107
  meta2[:parameters] = meta[:parameters].merge(meta2[:parameters])
102
108
  end
103
- @builder.build(parent_path: parent_path, meta: meta2, before_callbacks: before_callbacks, after_callbacks: after_callbacks)
109
+ @builder.build(parent_path: parent_path, meta: meta2, **kwargs)
104
110
  end
105
111
  end
106
112
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../application/linked_action'
4
+
5
+ module Meta
6
+ module RouteDSL
7
+ class AroundActionBuilder
8
+ def initialize
9
+ @around = []
10
+ end
11
+
12
+ def around(&block)
13
+ @around << block
14
+ end
15
+
16
+ def build
17
+ # 从后向前构建
18
+ @around.reverse.reduce(nil) do |following, p|
19
+ LinkedAction.new(p, following)
20
+ end
21
+ end
22
+
23
+ # 使用 before、after、around 系列和当前 action 共同构建洋葱圈模型。
24
+ # 构建成功后,执行顺序是:
25
+ #
26
+ # - before 序列
27
+ # - around 序列的前半部分
28
+ # - action
29
+ # - around 序列的后半部分
30
+ # - after 序列
31
+ #
32
+ def self.build(before: [], after: [], around: [], action: nil)
33
+ builder = AroundActionBuilder.new
34
+
35
+ # 首先构建 before 序列,保证它最先执行
36
+ builder.around do |next_action|
37
+ before.each { |p| self.instance_exec(&p) }
38
+ next_action.execute(self)
39
+ end unless before.empty?
40
+ # 然后构建 after 序列,保证它最后执行
41
+ builder.around do |next_action|
42
+ next_action.execute(self)
43
+ after.each { |p| self.instance_exec(&p) }
44
+ end unless after.empty?
45
+ # 接着应用洋葱圈模型,依次构建 around 序列、action
46
+ around.each { |p| builder.around(&p) }
47
+ builder.around { self.instance_exec(&action) } unless action.nil?
48
+
49
+ builder.build
50
+ end
51
+ end
52
+ end
53
+ 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,17 @@ 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
+ action = AroundActionBuilder.build(
51
+ action: @action_builder&.build,
52
+ **callbacks
53
+ )
51
54
 
52
55
  Route.new(
53
56
  path: @path,
54
57
  method: @method,
55
58
  meta: meta2,
56
- actions: actions
59
+ action: action
57
60
  )
58
61
  end
59
62
 
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.4"
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.4
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-18 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