tzispa 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/lib/tzispa/api/handler.rb +54 -23
  4. data/lib/tzispa/app.rb +60 -68
  5. data/lib/tzispa/cli.rb +42 -3
  6. data/lib/tzispa/commands/api.rb +55 -0
  7. data/lib/tzispa/commands/app.rb +83 -0
  8. data/lib/tzispa/commands/cli/generate.rb +60 -0
  9. data/lib/tzispa/commands/command.rb +28 -0
  10. data/lib/tzispa/commands/console.rb +62 -0
  11. data/lib/tzispa/commands/helpers/i18n.rb +67 -0
  12. data/lib/tzispa/commands/helpers/project.rb +69 -0
  13. data/lib/tzispa/commands/helpers/repository.rb +46 -0
  14. data/lib/tzispa/commands/project.rb +104 -0
  15. data/lib/tzispa/commands/repository.rb +66 -0
  16. data/lib/tzispa/commands/rig.rb +28 -0
  17. data/lib/tzispa/commands/server.rb +26 -0
  18. data/lib/tzispa/config/{appconfig.rb → app_config.rb} +12 -32
  19. data/lib/tzispa/config/base.rb +7 -5
  20. data/lib/tzispa/config/db_config.rb +67 -0
  21. data/lib/tzispa/config/yaml.rb +9 -10
  22. data/lib/tzispa/context.rb +3 -2
  23. data/lib/tzispa/controller/api.rb +66 -60
  24. data/lib/tzispa/controller/auth_layout.rb +4 -28
  25. data/lib/tzispa/controller/base.rb +61 -24
  26. data/lib/tzispa/controller/exceptions.rb +3 -4
  27. data/lib/tzispa/controller/http_error.rb +0 -3
  28. data/lib/tzispa/controller/layout.rb +4 -4
  29. data/lib/tzispa/domain.rb +27 -23
  30. data/lib/tzispa/env.rb +34 -0
  31. data/lib/tzispa/environment.rb +231 -0
  32. data/lib/tzispa/http/context.rb +65 -80
  33. data/lib/tzispa/http/request.rb +29 -17
  34. data/lib/tzispa/http/response.rb +45 -12
  35. data/lib/tzispa/route_set.rb +100 -0
  36. data/lib/tzispa/server.rb +61 -0
  37. data/lib/tzispa/tzisparc.rb +80 -0
  38. data/lib/tzispa/version.rb +1 -1
  39. data/lib/tzispa.rb +3 -1
  40. data/tzispa.gemspec +12 -6
  41. metadata +68 -17
  42. data/lib/tzispa/command/api.rb +0 -24
  43. data/lib/tzispa/command/app.rb +0 -95
  44. data/lib/tzispa/command/cli/generate.rb +0 -51
  45. data/lib/tzispa/command/project.rb +0 -258
  46. data/lib/tzispa/command/rig.rb +0 -26
  47. data/lib/tzispa/controller/signed_api.rb +0 -13
  48. data/lib/tzispa/http/session_flash_bag.rb +0 -62
  49. data/lib/tzispa/middleware.rb +0 -48
  50. data/lib/tzispa/routes.rb +0 -69
@@ -7,114 +7,120 @@ require 'tzispa/controller/exceptions'
7
7
  require 'tzispa/helpers/response'
8
8
  require 'tzispa/utils/string'
9
9
 
10
-
11
10
  module Tzispa
12
11
  module Controller
13
12
 
14
13
  class ControllerException < StandardError; end
15
14
 
16
15
  class Api < Base
17
-
18
- using Tzispa::Utils
16
+ using Tzispa::Utils::TzString
19
17
 
20
18
  include Tzispa::Helpers::Response
21
19
 
22
20
  def dispatch!
23
- handler_name, domain_name = context.router_params[:handler].split('.').reverse
24
- domain = domain_name.nil? ? context.app.domain : Tzispa::Domain.new(name: domain_name)
25
21
  verb = context.router_params[:verb]
26
22
  predicate = context.router_params[:predicate]
27
- handler = self.class.handler_class(domain, handler_name).new(context)
23
+ handler = prepare_handler
28
24
  handler.run! verb, predicate
29
- send(handler.response_verb, handler) if handler.response_verb
25
+ send(handler.type, handler) if handler.type
30
26
  response.finish
31
27
  end
32
28
 
33
- def redirect(handler)
34
- url = if handler.data && !handler.data.strip.empty?
35
- handler.data.start_with?('#') ? "#{request.referer}#{handler.data}" : handler.data
29
+ def prepare_handler
30
+ self.class.handler_class(request_method, domain, handler_name).new(context)
31
+ end
32
+
33
+ def domain
34
+ _, domain_name = context.router_params[:handler].split('.').reverse
35
+ domain_name ? Tzispa::Domain.new(name: domain_name) : context.app.domain
36
+ end
37
+
38
+ def handler_name
39
+ context.router_params[:handler].split('.').last
40
+ end
41
+
42
+ def handler_redirect_url(url)
43
+ if url && !url.strip.empty?
44
+ url.start_with?('#') ? "#{request.referer}#{url}" : url
36
45
  else
37
46
  request.referer
38
- end
39
- context.flash << handler.message if config.sessions&.enabled && handler.error?
40
- context.redirect url, config.absolute_redirects, response
47
+ end
48
+ end
49
+
50
+ def redirect(handler)
51
+ api_flash(handler.message) if handler.error?
52
+ context.redirect handler.redirect_url(handler.data), config.absolute_redirects, response
41
53
  end
42
54
 
43
55
  def html(handler)
44
- content_type :htm
45
- context.flash << handler.message if config.sessions&.enabled && handler.error?
46
- response.body << handler.data
47
- set_api_headers handler.status
56
+ content = handler.error? ? handler.message : handler.data
57
+ api_response :htm, content, handler.status, handler.error
48
58
  end
49
59
 
50
60
  def json(handler)
51
- content_type :json
52
- data = ::String === handler.data ? JSON.parse(handler.data) : handler.data.to_json
53
- unless handler.error?
54
- response.body << data
55
- else
56
- response.body << Hash[:__error, true, :__error_msg, handler.message, :__error_code, handler.status].to_json
57
- end
58
- set_api_headers handler.status
61
+ content = if handler.error?
62
+ { error_message: handler.message,
63
+ error_code: handler.error }.to_json
64
+ else
65
+ handler.data.is_a?(::String) ? JSON.parse(handler.data) : handler.data.to_json
66
+ end
67
+ api_response :json, content, handler.status, handler.error
59
68
  end
60
69
 
61
70
  def text(handler)
62
- content_type :text
63
- context.flash << handler.message if config.sessions&.enabled && handler.error?
64
- response.body << handler.data
65
- set_api_headers handler.status
71
+ content = handler.error? ? handler.message : handler.data
72
+ api_response :text, content, handler.status, handler.error
66
73
  end
67
74
 
68
75
  def download(handler)
69
76
  send_file handler.data[:path], handler.data
70
77
  end
71
78
 
72
- class << self
79
+ def api_flash(message)
80
+ context.flash << message if config.sessions&.enabled
81
+ end
82
+
83
+ def api_response(type, content, status = nil, error = nil)
84
+ content_type type
85
+ response.body << content
86
+ response.status = status if status
87
+ api_headers error
88
+ end
73
89
 
90
+ def request_method
91
+ context.request_method.downcase
92
+ end
93
+
94
+ class << self
74
95
  def handler_class_name(handler_name)
75
96
  "#{handler_name.camelize}Handler"
76
97
  end
77
98
 
78
- def handler_class_file(domain, handler_name)
79
- "#{domain.path}/api/#{handler_name}.rb"
99
+ def handler_class_file(domain, handler_name, request_method)
100
+ "#{domain.path}/api/#{request_method}/#{handler_name}.rb"
80
101
  end
81
102
 
82
- def handler_namespace(domain)
83
- "#{domain.name.to_s.camelize}::Api"
103
+ def handler_namespace(domain, request_method)
104
+ "#{domain.name.to_s.camelize}::Api::#{request_method.capitalize}"
84
105
  end
85
106
 
86
- def handler_class(domain, handler_name)
87
- domain.require "api/#{handler_name}"
88
- "#{handler_namespace domain}::#{handler_class_name handler_name}".constantize
107
+ def handler_class(request_method, domain, handler_name)
108
+ domain.require "api/#{request_method}/#{handler_name}"
109
+ "#{handler_namespace domain, request_method}::#{handler_class_name handler_name}"
110
+ .constantize
89
111
  end
90
-
91
- def generate_handler(domain, name)
92
- raise "The handler '#{name}' already exist" if File.exist?(handler_class_file)
93
- File.open(handler_class_file(domain, name), "w") { |f|
94
- handler_code = String.new
95
- f.puts handler_code.indenter("require 'tzispa/api/handler'\n\n")
96
- level = 0
97
- handler_namespace.split('::').each { |ns|
98
- f.puts handler_code.indenter("module #{ns}\n", level > 0 ? 2 : 0).to_s
99
- level += 1
100
- }
101
- f.puts handler_code.indenter("\nclass #{handler_class_name} < Tzispa::Api::Handler\n\n", 2)
102
- f.puts handler_code.indenter("end\n\n")
103
- handler_namespace.split('::').each { |ns|
104
- f.puts handler_code.unindenter("end\n", 2)
105
- }
106
- }
107
- end
108
-
109
112
  end
110
113
 
111
114
  private
112
115
 
113
- def set_api_headers(status)
114
- response['X-API'] = "#{context.router_params[:handler]}:#{context.router_params[:verb]}:#{context.router_params[:predicate]}"
115
- response['X-API-STATE'] = "#{status}"
116
+ def api_headers(error = nil)
117
+ handler = context.router_params[:handler]
118
+ verb = context.router_params[:verb]
119
+ predicate = context.router_params[:predicate]
120
+ response['X-API'] = "#{handler}:#{verb}:#{predicate}"
121
+ response['X-API-SR'] = error.to_s if error
116
122
  end
117
-
118
123
  end
124
+
119
125
  end
120
126
  end
@@ -1,37 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'tzispa_rig'
4
- require 'tzispa/controller/base'
5
- require 'tzispa/controller/exceptions'
6
- require 'tzispa/helpers/response'
7
- require 'tzispa_rig'
3
+ require 'tzispa/controller/layout'
8
4
 
9
5
  module Tzispa
10
6
  module Controller
11
- class AuthLayout < Base
12
- include Tzispa::Helpers::Response
13
-
14
- def render!
15
- if (layout_name == login_layout) || context.logged?
16
- rig = Tzispa::Rig::Engine.layout name: layout_name, domain: application.domain, content_type: context.router_params[:format] || config.default_format
17
- response.body << rig.render(context)
18
- content_type rig.content_type
19
- else
20
- context.redirect login_layout, true, response
21
- end
22
- end
23
-
24
- private
25
-
26
- def layout_name
27
- context.layout || config.default_layout
28
- end
29
-
30
- def login_layout
31
- config.login_layout
32
- end
33
-
34
7
 
8
+ class AuthLayout < Layout
9
+ before :login_redirect
35
10
  end
11
+
36
12
  end
37
13
  end
@@ -2,7 +2,8 @@
2
2
 
3
3
  require 'forwardable'
4
4
  require 'tzispa/helpers/error_view'
5
-
5
+ require 'tzispa/http/context'
6
+ require 'tzispa/environment'
6
7
 
7
8
  module Tzispa
8
9
  module Controller
@@ -12,48 +13,84 @@ module Tzispa
12
13
 
13
14
  include Tzispa::Helpers::ErrorView
14
15
 
15
- attr_reader :context, :application
16
- def_delegators :@context, :request, :response, :config
16
+ attr_reader :context, :application, :callmethod
17
+ def_delegators :@context, :request, :response, :config,
18
+ :login_redirect, :unauthorized_but_logged
17
19
 
18
- def initialize(app, callmethod=nil)
20
+ def initialize(app, callmethod = nil)
19
21
  @callmethod = callmethod
20
22
  @application = app
21
23
  end
22
24
 
23
25
  def call(env)
24
26
  @context = Tzispa::Http::Context.new(@application, env)
25
- #env[Tzispa::ENV_TZISPA_CONTEXT] = @context
26
- #@context = env[Tzispa::ENV_TZISPA_CONTEXT]
27
- invoke @callmethod if @callmethod
27
+ invoke if callmethod
28
28
  response.finish
29
29
  end
30
30
 
31
+ class << self
32
+ def before(*args)
33
+ (@before_chain ||= []).tap do |bef|
34
+ args&.each do |s|
35
+ s = s.to_sym
36
+ bef << s unless bef.include?(s)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
31
42
  private
32
43
 
33
- def invoke(callmethod)
34
- debug_info = nil
35
- status = catch(:halt) {
36
- begin
37
- send "#{@callmethod}"
38
- rescue Tzispa::Rig::NotFound => ex
39
- context.logger.info "#{ex.message} (#{ex.class})"
40
- 404
41
- rescue StandardError, ScriptError => ex
42
- context.logger.error "#{ex.message} (#{ex.class}):\n #{ex.backtrace.join("\n\t") if ex.respond_to?(:backtrace) && ex.backtrace}"
43
- debug_info = debug_info(ex) if config.developing
44
- 500
45
- end
44
+ def invoke
45
+ prepare_response catch(:halt) {
46
+ do_before
47
+ send callmethod
46
48
  }
49
+ rescue Tzispa::Rig::NotFound => ex
50
+ prepare_client_error(404, ex)
51
+ rescue StandardError, ScriptError, SecurityError => ex
52
+ prepare_server_error(500, ex)
53
+ end
54
+
55
+ def prepare_response(status, content = nil)
47
56
  response.status = status if status.is_a?(Integer)
48
57
  if response.client_error?
49
- response.body = error_page(context.domain, status: response.status)
58
+ prepare_client_error(status)
50
59
  elsif response.server_error?
51
- response.body = debug_info ?
52
- debug_info :
53
- error_page(context.domain, status: response.status)
60
+ prepare_server_error(status)
61
+ elsif content
62
+ response.body = content
63
+ end
64
+ end
65
+
66
+ def do_before
67
+ self.class.before.each { |hook| send hook }
68
+ end
69
+
70
+ def prepare_client_error(status, error = nil)
71
+ status.tap do |code|
72
+ context.logger.info log_format(code, error.to_s) if error
73
+ response.body = error_page(context.domain, status: code)
54
74
  end
55
75
  end
56
76
 
77
+ def prepare_server_error(status, error = nil)
78
+ status.tap do |code|
79
+ context.logger.error log_format(code, error_log(error)) if error
80
+ response.body = if error && Tzispa::Environment.development?
81
+ debug_info(error)
82
+ else
83
+ error_page(context.domain, status: code)
84
+ end
85
+ end
86
+ end
87
+
88
+ def log_format(status, msg)
89
+ String.new.tap do |str|
90
+ str << "[#{context.request.ip} #{DateTime.now}] #{context.request.request_method}"
91
+ str << " #{context.request.fullpath} #{status}\n#{msg}"
92
+ end
93
+ end
57
94
  end
58
95
 
59
96
  end
@@ -2,10 +2,9 @@ module Tzispa
2
2
  module Controller
3
3
  module Error
4
4
 
5
- class ControllerError < StandardError; end;
6
- class Http < ControllerError; end;
7
- class Http_404 < Http; end;
8
- class InvalidSign < ControllerError; end;
5
+ class ControllerError < StandardError; end
6
+ class Http < ControllerError; end
7
+ class InvalidSign < ControllerError; end
9
8
 
10
9
  end
11
10
  end
@@ -7,14 +7,11 @@ module Tzispa
7
7
  module Controller
8
8
 
9
9
  class HttpError < Base
10
-
11
10
  include Tzispa::Helpers::Response
12
11
 
13
12
  def error_404
14
13
  not_found
15
14
  end
16
-
17
-
18
15
  end
19
16
 
20
17
  end
@@ -4,7 +4,6 @@ require 'tzispa_rig'
4
4
  require 'tzispa/controller/base'
5
5
  require 'tzispa/controller/exceptions'
6
6
  require 'tzispa/helpers/response'
7
- require 'tzispa_rig'
8
7
 
9
8
  module Tzispa
10
9
  module Controller
@@ -12,7 +11,9 @@ module Tzispa
12
11
  include Tzispa::Helpers::Response
13
12
 
14
13
  def render!
15
- rig = Tzispa::Rig::Engine.layout name: layout_name, domain: application.domain, content_type: context.router_params[:format] || config.default_format
14
+ rig = Tzispa::Rig::Engine.layout name: layout_name,
15
+ domain: application.domain,
16
+ content_type: context.router_params[:format] || config.default_format
16
17
  response.body << rig.render(context)
17
18
  content_type rig.content_type
18
19
  end
@@ -22,8 +23,7 @@ module Tzispa
22
23
  def layout_name
23
24
  context.layout || config.default_layout
24
25
  end
25
-
26
-
27
26
  end
27
+
28
28
  end
29
29
  end
data/lib/tzispa/domain.rb CHANGED
@@ -3,47 +3,52 @@
3
3
  require 'tzispa/utils/string'
4
4
 
5
5
  module Tzispa
6
- class Domain
7
6
 
8
- using Tzispa::Utils
7
+ class Domain
8
+ using Tzispa::Utils::TzString
9
9
 
10
10
  attr_reader :name, :root
11
11
 
12
- DEFAULT_DOMAIN_NAME = :default
13
- DEFAULT_DOMAINS_ROOT = :apps
14
-
15
-
16
- def initialize(name=DEFAULT_DOMAIN_NAME, root=DEFAULT_DOMAINS_ROOT)
12
+ def initialize(name)
17
13
  @name = name
18
- @root = root
14
+ @root = "#{Tzispa::Environment.instance.root}/#{Tzispa::Environment.instance.apps_path}"
15
+ instance_eval "module ::#{name.to_s.capitalize}; end"
16
+ end
17
+
18
+ def setup
19
+ require_dir
20
+ require_dir 'helpers'
21
+ require_dir 'services'
22
+ require_dir 'api'
23
+ require_dir 'middleware'
19
24
  end
20
25
 
21
26
  def path
22
- "#{root.to_s.downcase}/#{name.to_s.downcase}".freeze
27
+ @path ||= root % name.to_s.downcase
23
28
  end
24
29
 
25
30
  def require(file)
26
- Kernel.require "./#{path}/#{file}"
31
+ Kernel.require "#{path}/#{file}"
27
32
  end
28
33
 
29
34
  def load(file)
30
- Kernel.load "./#{path}/#{file}.rb"
35
+ Kernel.load "#{path}/#{file}.rb"
31
36
  end
32
37
 
33
38
  def require_dir(dir = nil)
34
- rqpath = dir ? "/#{path}/#{dir}" : "/#{path}"
35
- Dir[".#{rqpath}/*.rb"].each { |file|
39
+ rqpath = dir ? "#{path}/#{dir}" : path.to_s
40
+ Dir["#{rqpath}/*.rb"].each do |file|
36
41
  name = file.split('/').last.split('.').first
37
- Kernel.require ".#{rqpath}/#{name}"
38
- }
42
+ Kernel.require "#{rqpath}/#{name}"
43
+ end
39
44
  end
40
45
 
41
46
  def load_dir(dir = nil)
42
- rqpath = dir ? "/#{path}/#{dir}" : "/#{path}"
43
- Dir[".#{rqpath}/*.rb"].each { |file|
47
+ rqpath = dir ? "#{path}/#{dir}" : path.to_s
48
+ Dir["#{rqpath}/*.rb"].each do |file|
44
49
  name = file.split('/').last
45
- Kernel.load ".#{rqpath}/#{name}"
46
- }
50
+ Kernel.load "#{rqpath}/#{name}"
51
+ end
47
52
  end
48
53
 
49
54
  def include(cmod)
@@ -51,13 +56,12 @@ module Tzispa
51
56
  end
52
57
 
53
58
  def self.require(domain, file)
54
- self.new(name: domain).require(file)
59
+ new(name: domain).require(file)
55
60
  end
56
61
 
57
62
  def self.load(domain, file)
58
- self.new(name: domain).load(file)
63
+ new(name: domain).load(file)
59
64
  end
60
-
61
-
62
65
  end
66
+
63
67
  end
data/lib/tzispa/env.rb ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dotenv'
4
+
5
+ module Tzispa
6
+
7
+ class Env
8
+ def initialize(env: ENV)
9
+ @env = env
10
+ end
11
+
12
+ def [](key)
13
+ @env[key]
14
+ end
15
+
16
+ def []=(key, value)
17
+ @env[key] = value
18
+ end
19
+
20
+ def load!(path)
21
+ return unless defined?(Dotenv)
22
+
23
+ contents = ::File.open(path, 'rb:bom|utf-8', &:read)
24
+ parsed = Dotenv::Parser.call(contents)
25
+
26
+ parsed.each do |k, v|
27
+ next if @env.key?(k)
28
+ @env[k] = v
29
+ end
30
+ nil
31
+ end
32
+ end
33
+
34
+ end