tzispa 0.6.1 → 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.
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