zleb 0.1.6

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.
@@ -0,0 +1,33 @@
1
+ #转换为snake
2
+ require 'plissken'
3
+
4
+
5
+ class Roda
6
+ module RodaPlugins
7
+
8
+ def self.load_dependencies(app)
9
+ app.plugin :request_headers
10
+ app.plugin :json_parser
11
+ end
12
+ module ParamsSnakeKeys
13
+
14
+ def self.configure(config)
15
+
16
+ end
17
+
18
+ module RequestMethods
19
+ def params
20
+ super_params = super
21
+ if headers['X-Convert-Key'] == 'true'
22
+ super_params.to_snake_keys
23
+ else
24
+ super_params
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ register_plugin(:params_snake_keys, ParamsSnakeKeys)
32
+ end
33
+ end
@@ -0,0 +1,142 @@
1
+ # frozen-string-literal: true
2
+
3
+ require 'uri'
4
+
5
+ begin
6
+ require 'rack/files'
7
+ rescue LoadError
8
+ require 'rack/file'
9
+ end
10
+
11
+ #
12
+ class Roda
13
+ module RodaPlugins
14
+ # The public plugin adds a +r.public+ routing method to serve static files
15
+ # from a directory.
16
+ #
17
+ # The public plugin recognizes the application's :root option, and defaults to
18
+ # using the +public+ subfolder of the application's +:root+ option. If the application's
19
+ # :root option is not set, it defaults to the +public+ folder in the working
20
+ # directory. Additionally, if a relative path is provided as the +:root+
21
+ # option to the plugin, it will be considered relative to the application's
22
+ # +:root+ option.
23
+ #
24
+ # Examples:
25
+ #
26
+ # # Use public folder as location of files
27
+ # plugin :public
28
+ #
29
+ # # Use /path/to/app/static as location of files
30
+ # opts[:root] = '/path/to/app'
31
+ # plugin :public, root: 'static'
32
+ #
33
+ # # Assuming public is the location of files
34
+ # r.route do
35
+ # # Make GET /images/foo.png look for public/images/foo.png
36
+ # r.public
37
+ #
38
+ # # Make GET /static/images/foo.png look for public/images/foo.png
39
+ # r.on(:static) do
40
+ # r.public
41
+ # end
42
+ # end
43
+ module RenderStatic
44
+ SPLIT = Regexp.union(*[File::SEPARATOR, File::ALT_SEPARATOR].compact)
45
+ PARSER = URI::DEFAULT_PARSER
46
+
47
+ RACK_FILES = defined?(Rack::Files) ? Rack::Files : Rack::File
48
+ # Use options given to setup a Rack::File instance for serving files. Options:
49
+ # :default_mime :: The default mime type to use if the mime type is not recognized.
50
+ # :gzip :: Whether to serve already gzipped files with a .gz extension for clients
51
+ # supporting gzipped transfer encoding.
52
+ # :headers :: A hash of headers to use for statically served files
53
+ # :root :: Use this option for the root of the public directory (default: "public")
54
+ def self.configure(app, opts={})
55
+ if opts[:root]
56
+ app.opts[:public_root] = app.expand_path(opts[:root])
57
+ elsif !app.opts[:public_root]
58
+ app.opts[:public_root] = app.expand_path("public")
59
+ end
60
+ app.opts[:public_server] = RACK_FILES.new(app.opts[:public_root], opts[:headers]||{}, opts[:default_mime] || 'text/plain')
61
+ app.opts[:public_gzip] = opts[:gzip]
62
+ end
63
+
64
+ module RequestMethods
65
+ # Serve files from the public directory if the file exists and this is a GET request.
66
+ def render_static(render_path)
67
+ if is_get?
68
+ path = PARSER.unescape(render_path)
69
+ return if path.include?("\0")
70
+
71
+ roda_opts = roda_class.opts
72
+ server = roda_opts[:public_server]
73
+ path = ::File.join(server.root, *public_path_segments(path))
74
+
75
+ if roda_opts[:public_gzip] && env['HTTP_ACCEPT_ENCODING'] =~ /\bgzip\b/
76
+ gzip_path = path + '.gz'
77
+
78
+ if public_file_readable?(gzip_path)
79
+ res = public_serve(server, gzip_path)
80
+ headers = res[1]
81
+
82
+ unless res[0] == 304
83
+ if mime_type = ::Rack::Mime.mime_type(::File.extname(path), 'text/plain')
84
+ headers['Content-Type'] = mime_type
85
+ end
86
+ headers['Content-Encoding'] = 'gzip'
87
+ end
88
+
89
+ halt res
90
+ end
91
+ end
92
+
93
+ if public_file_readable?(path)
94
+ halt public_serve(server, path)
95
+ end
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ # Return an array of segments for the given path, handling ..
102
+ # and . components
103
+ def public_path_segments(path)
104
+ segments = []
105
+
106
+ path.split(SPLIT).each do |seg|
107
+ next if seg.empty? || seg == '.'
108
+ seg == '..' ? segments.pop : segments << seg
109
+ end
110
+
111
+ segments
112
+ end
113
+
114
+ # Return whether the given path is a readable regular file.
115
+ def public_file_readable?(path)
116
+ ::File.file?(path) && ::File.readable?(path)
117
+ rescue SystemCallError
118
+ # :nocov:
119
+ false
120
+ # :nocov:
121
+ end
122
+
123
+ if ::Rack.release > '2'
124
+ # Serve the given path using the given Rack::File server.
125
+ def public_serve(server, path)
126
+ server.serving(self, path)
127
+ end
128
+ else
129
+ # :nocov:
130
+ def public_serve(server, path)
131
+ server = server.dup
132
+ server.path = path
133
+ server.serving(env)
134
+ end
135
+ # :nocov:
136
+ end
137
+ end
138
+ end
139
+
140
+ register_plugin(:render_static, RenderStatic)
141
+ end
142
+ end
@@ -0,0 +1,293 @@
1
+
2
+ #dry schema enhance
3
+ require_relative "dry_schema_enhance.rb"
4
+
5
+ module DrySchemaExtend
6
+ class DrySchemaExendError < Exception
7
+ end
8
+
9
+ class SchemaInfoCompiler
10
+ EMPTY_HASH = {}
11
+ PREDICATE_TO_TYPE = {
12
+ array?: "array",
13
+ bool?: "bool",
14
+ date?: "date",
15
+ date_time?: "date_time",
16
+ decimal?: "decimal",
17
+ float?: "float",
18
+ hash?: "hash",
19
+ int?: "integer",
20
+ nil?: "nil",
21
+ str?: "string",
22
+ time?: "time",
23
+ }.freeze
24
+
25
+ # @api private
26
+ attr_reader :keys
27
+
28
+ # @api private
29
+ def initialize(schema)
30
+ @keys = EMPTY_HASH.dup
31
+ @schema = schema
32
+ end
33
+
34
+ # @api private
35
+ def to_h
36
+ { children: @keys, type: "hash" }
37
+ end
38
+
39
+ # @api private
40
+ def call
41
+ visit(@schema.to_ast)
42
+ end
43
+
44
+ # @api private
45
+ def visit(node, opts = EMPTY_HASH)
46
+ meth, rest = node
47
+ public_send(:"visit_#{meth}", rest, opts)
48
+ end
49
+
50
+ # @api private
51
+ def visit_set(node, opts = EMPTY_HASH)
52
+ target = (key = opts[:key]) ? self.class.new(@schema) : self
53
+
54
+ node.map { |child| target.visit(child, opts) }
55
+
56
+ return unless key
57
+
58
+ target_info = opts[:member] ? { member: target.to_h } : target.to_h
59
+ type = opts[:member] ? "array" : "hash"
60
+
61
+ keys.update(key => { **keys[key], type: type, **target_info })
62
+ end
63
+
64
+ # @api private
65
+ def visit_and(node, opts = EMPTY_HASH)
66
+ left, right = node
67
+
68
+ visit(left, opts)
69
+ visit(right, opts)
70
+ end
71
+
72
+ # @api private
73
+ def visit_implication(node, opts = EMPTY_HASH)
74
+ node.each do |el|
75
+ # p "--------------"
76
+ # p el
77
+ if el[0] == :predicate and el[1][0] == :key?
78
+ visit(el, opts.merge(required: false))
79
+ else
80
+ visit(el, opts)
81
+ end
82
+ end
83
+ end
84
+
85
+ def visit_not(node, opts= EMPTY_HASH)
86
+ visit(node, opts.merge(not: true))
87
+ end
88
+
89
+ # @api private
90
+ def visit_each(node, opts = EMPTY_HASH)
91
+ visit(node, opts.merge(member: true))
92
+ end
93
+
94
+ # @api private
95
+ def visit_key(node, opts = EMPTY_HASH)
96
+
97
+ name, rest = node
98
+ # puts "visit_key is ### #{name}"
99
+ visit(rest, opts.merge(key: name, required: true, member: false)) # modified by zhanghz
100
+ end
101
+
102
+ # @api private
103
+ def visit_predicate(node, opts = EMPTY_HASH)
104
+ name, rest = node
105
+
106
+ key = opts[:key]
107
+
108
+ if name.equal?(:key?)
109
+ # p "---key is #{rest[0][1]} #{opts}"
110
+ keys[rest[0][1]] = { required: opts.fetch(:required, true) }
111
+ else
112
+ type = PREDICATE_TO_TYPE[name]
113
+ # p opts
114
+ # p "rest predicate key is #{key} content is #{name}| #{rest}"
115
+
116
+ # modified by zhanghz
117
+ if opts[:member] == true
118
+ keys[key][:member] ||= {}
119
+ if type
120
+ if type == 'nil' and opts[:not] == true
121
+ keys[key][:member][:maybe] = type
122
+ else
123
+ keys[key][:member][:type] = type
124
+ end
125
+ keys[key][:type] = "array"
126
+ else
127
+ keys[key][:member][:validations] = [] unless keys[key][:member][:validations]
128
+ keys[key][:member][:validations] << predicate_message(key, name, rest)
129
+ end
130
+
131
+ else
132
+ if type
133
+ if type == 'nil' and opts[:not] == true
134
+ keys[key][:maybe] = type
135
+ else
136
+ keys[key][:type] = type
137
+ end
138
+ else
139
+ keys[key][:validations] = [] unless keys[key][:validations]
140
+ keys[key][:validations] << predicate_message(key, name, rest)
141
+ end
142
+ end
143
+ end
144
+ end
145
+
146
+ def predicate_message(key, name, rest)
147
+ result = nil
148
+ if rest.count == 2
149
+ param_name = rest[0][0]
150
+ param_value = rest[0][1]
151
+
152
+ result = @schema.message_compiler.messages.get_message_for_key(name,{:path=> key, :arg_type => param_value.class})
153
+ if param_value.class == Range
154
+ result = result[:text].gsub("%{#{param_name}_left}", param_value.first.to_s).gsub("%{#{param_name}_right}", param_value.last.to_s)
155
+ else
156
+ result = result[:text].gsub("%{#{param_name}}", param_value.to_s)
157
+ end
158
+ else
159
+ result = @schema.message_compiler.messages.get_message_for_key(name,{:path=> key})
160
+ result = result[:text]
161
+ end
162
+ result
163
+ end
164
+ end
165
+
166
+
167
+ class SchemaExtendEdInfo
168
+
169
+ def self.define(&block)
170
+ desc = self.new
171
+ desc.instance_eval(&block)
172
+ desc
173
+ end
174
+
175
+ def initialize()
176
+ @schema_desc = {}
177
+ @current_field_name = nil
178
+ end
179
+
180
+
181
+ def required(field_name)
182
+ key(:required, field_name)
183
+ end
184
+
185
+ def optional(field_name)
186
+ key(:optional, field_name)
187
+ end
188
+
189
+ def hash(&block)
190
+ instance_eval(&block)
191
+ self
192
+ end
193
+
194
+
195
+ def get_desc
196
+ @schema_desc.transform_values do |field|
197
+ field.get_desc
198
+ end
199
+ end
200
+
201
+
202
+
203
+
204
+ private
205
+ def key(type, name)
206
+ if @schema_desc.key?(name)
207
+ raise DrySchemaExendError.new("the key: #{name} already exists")
208
+ end
209
+ field_desc = FieldExtendedInfo.new(name)
210
+ @schema_desc[name] = field_desc
211
+ @current_field_name = name
212
+ field_desc
213
+ end
214
+
215
+
216
+ end
217
+
218
+
219
+ class FieldExtendedInfo
220
+
221
+ def initialize(field_name)
222
+ @field_name = field_name
223
+ @desc = nil
224
+ end
225
+
226
+ def desc(**info)
227
+ @desc = info
228
+ self
229
+ end
230
+
231
+
232
+ def get_desc
233
+ result = {}
234
+ if @children
235
+ if @is_member
236
+ result[:member] = {}
237
+ result[:member][:children] = @children.get_desc
238
+ else
239
+ result[:children] = @children.get_desc
240
+ end
241
+ end
242
+ result[:desc] = @desc if @desc
243
+ result
244
+ end
245
+
246
+ def method_missing(method_name, *args, &block)
247
+ rest_method(method_name, *args, &block)
248
+ end
249
+
250
+ def hash(*arg, &block)
251
+ rest_method(:hash, *arg, &block)
252
+ end
253
+
254
+
255
+ private
256
+ def rest_method(method_name, *args, &block)
257
+ if block_given?
258
+ sub_schema_desc = SchemaExtendEdInfo.new
259
+ sub_schema_desc.instance_eval(&block)
260
+ @children = sub_schema_desc
261
+ if method_name == :array or method_name == :each
262
+ @is_member = true
263
+ end
264
+ end
265
+
266
+ if args[0].class == SchemaExtendEdInfo
267
+ @children = args[0]
268
+ end
269
+ self
270
+ end
271
+ end
272
+ end
273
+
274
+
275
+
276
+ module Dry
277
+ module Schema
278
+ module Macros
279
+ # Macro specialization used within the DSL
280
+ #
281
+ # @api public
282
+ class DSL
283
+ def desc(*args)
284
+ self
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
290
+
291
+
292
+
293
+
@@ -0,0 +1,18 @@
1
+ require 'semantic_logger'
2
+
3
+ class Roda
4
+ module RodaPlugins
5
+ module UniLogger
6
+
7
+ module InstanceMethods
8
+ include SemanticLogger::Loggable
9
+ end
10
+
11
+ module ClassMethods
12
+ include SemanticLogger::Loggable
13
+ end
14
+ end
15
+
16
+ register_plugin(:uni_logger, UniLogger)
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ module SymbolizeHelper
2
+ extend self
3
+ def symbolize_recursive(hash)
4
+ {}.tap do |h|
5
+ hash.each { |key, value| h[key.to_sym] = map_value(value) }
6
+ end
7
+ end
8
+
9
+ def map_value(thing)
10
+ case thing
11
+ when Hash
12
+ symbolize_recursive(thing)
13
+ when Array
14
+ thing.map { |v| map_value(v) }
15
+ else
16
+ thing
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Zleb
2
+ VERSION = "0.1.6"
3
+ end
data/lib/zleb.rb ADDED
@@ -0,0 +1,133 @@
1
+ require "zleb/version"
2
+ require "zleb/symbolize_helper"
3
+ require "zleb/controller"
4
+
5
+ require 'roda'
6
+
7
+ # for logging
8
+ require 'semantic_logger'
9
+
10
+ if ENV['RACK_ENV'] == 'development'
11
+ SemanticLogger.default_level = :trace # :trace
12
+ # SemanticLogger.backtrace_level = :trace
13
+ # SemanticLogger.add_appender(io: $stderr, level: :error)
14
+ SemanticLogger.add_appender(io: $stdout, formatter: :color)
15
+ end
16
+
17
+ # add logger to Object
18
+ class Object
19
+ include SemanticLogger::Loggable
20
+ end
21
+
22
+ require 'roda/session_middleware'
23
+
24
+ require 'zleb/plugins/params_check'
25
+ require 'zleb/plugins/render_static'
26
+ require 'zleb/plugins/uni_logger'
27
+ require 'zleb/plugins/params_snake_keys'
28
+ require 'zleb/plugins/basic_restful_action'
29
+ require 'zleb/plugins/api_route'
30
+ require 'zleb/plugins/method_route'
31
+ require 'zleb/plugins/http_auth'
32
+
33
+
34
+ #转换为驼峰
35
+ require 'awrence'
36
+ #转换为snake
37
+ require 'plissken'
38
+
39
+ module Zleb
40
+ class Error < StandardError; end
41
+
42
+
43
+ module Config
44
+ def self.prefix
45
+ @prefix || 'api'
46
+ end
47
+
48
+ def self.prefix=(pre)
49
+ @prefix = pre
50
+ end
51
+
52
+ def self.controller_module
53
+ @controller_module
54
+ end
55
+
56
+ def self.controller_module=(c)
57
+ @controller_module = c
58
+ end
59
+
60
+ def self.set_global_check(&block)
61
+ @global_check = block
62
+ end
63
+
64
+ def self.global_check=(c)
65
+ @global_check = c
66
+ end
67
+
68
+ def self.global_check
69
+ @global_check
70
+ end
71
+ end
72
+
73
+
74
+ class BaseApp < Roda
75
+
76
+ plugin :empty_root
77
+ plugin :all_verbs
78
+
79
+ json_serializer = proc do |res, req|
80
+ if req.headers['X-Convert-Key'] == 'true'
81
+ res.to_camelback_keys.to_json
82
+ else
83
+ res.to_json
84
+ end
85
+ end
86
+
87
+ plugin :request_headers
88
+ plugin :json, include_request: true, classes: [Array, Hash], serializer: json_serializer
89
+ plugin :params_snake_keys
90
+
91
+ plugin :json_parser #, parser: proc{|req| JSON.parse(req).to_snake_keys}
92
+ plugin :hash_routes
93
+ plugin :public
94
+ plugin :params_check
95
+ plugin :basic_restful_action
96
+ plugin :render_static
97
+
98
+ plugin :uni_logger
99
+ plugin :cookies
100
+ plugin :symbolized_params
101
+
102
+
103
+ plugin :api_route
104
+ # plugin :error_handler, classes: [Mongoid::Errors::DocumentNotFound] do |e|
105
+ # response.status = 406
106
+ # { error: "object is not existed" }
107
+ # end
108
+
109
+ # plugin :not_found do |r|
110
+ # r.render_static(File.join(Prefix, 'index.html'))
111
+ # # {error: "not found #{r.path}"}
112
+ # end
113
+
114
+ secret = ENV.delete('SESSION_SECRET') || SecureRandom.random_bytes(64)
115
+ use RodaSessionMiddleware, secret: secret
116
+ end
117
+
118
+ class App < BaseApp
119
+ plugin :method_route
120
+
121
+ GlobalCheck =
122
+
123
+ route do |r|
124
+ r.public
125
+ r.on Config.prefix do
126
+ instance_eval(&Config.global_check) if Config.global_check
127
+ r.method_to Config.controller_module if Config.controller_module
128
+ r.hash_routes(:api)
129
+ end
130
+ end
131
+
132
+ end
133
+ end
data/push.sh ADDED
@@ -0,0 +1,3 @@
1
+ # curl -d '_method=DELETE' -X POST http://192.168.1.132:9571/gems/zleb-0.1.0.gem
2
+
3
+ gem inabox pkg/zleb-0.1.6.gem --host http://localhost:8081/repository/groupgems/
data/zleb.gemspec ADDED
@@ -0,0 +1,43 @@
1
+ require_relative 'lib/zleb/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "zleb"
5
+ spec.version = Zleb::VERSION
6
+ spec.authors = ["saidev"]
7
+ spec.email = ["saidev@163.com"]
8
+
9
+ spec.summary = %q{a more convenient web framework}
10
+ spec.description = %q{a more convenient web framework}
11
+ spec.homepage = "https://github.com/bluesaiz/zleb"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = spec.homepage
17
+ spec.metadata["changelog_uri"] = spec.homepage
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_dependency "roda", '~> 3.52'
29
+ spec.add_dependency "roda-symbolized_params", '~> 0.0.2'
30
+ spec.add_dependency "dry-validation", '~> 1.6'
31
+ spec.add_dependency 'puma', '~> 6.4'
32
+ spec.add_dependency "semantic_logger", "~> 4.7", ">= 4.7.4"
33
+
34
+ spec.add_dependency "awrence", "~> 1.2", ">= 1.2.1"
35
+ spec.add_dependency "plissken", "~> 1.4", ">= 1.4.1"
36
+ spec.add_dependency "concurrent-ruby"
37
+
38
+ spec.add_dependency "dry-schema", "= 1.13.3"
39
+ spec.add_dependency "dry-container", "~> 0.7"
40
+
41
+ spec.add_development_dependency 'rack-test'
42
+
43
+ end