tzispa 0.3.3
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 +7 -0
- data/CHANGELOG.md +55 -0
- data/README.md +3 -0
- data/lib/tzispa/api/handler.rb +64 -0
- data/lib/tzispa/app.rb +93 -0
- data/lib/tzispa/config/base.rb +15 -0
- data/lib/tzispa/config/routes.rb +55 -0
- data/lib/tzispa/config/webconfig.rb +37 -0
- data/lib/tzispa/config/yaml.rb +18 -0
- data/lib/tzispa/controller/api.rb +96 -0
- data/lib/tzispa/controller/base.rb +89 -0
- data/lib/tzispa/controller/exceptions.rb +12 -0
- data/lib/tzispa/controller/layout.rb +43 -0
- data/lib/tzispa/data/entity.rb +40 -0
- data/lib/tzispa/domain.rb +39 -0
- data/lib/tzispa/http/context.rb +82 -0
- data/lib/tzispa/http/request.rb +33 -0
- data/lib/tzispa/http/response.rb +65 -0
- data/lib/tzispa/http/session_flash_bag.rb +62 -0
- data/lib/tzispa/middleware.rb +82 -0
- data/lib/tzispa/version.rb +7 -0
- data/lib/tzispa.rb +7 -0
- metadata +163 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 47b98de1bcd11030f582a0f8d96de15eaba48d5d
|
4
|
+
data.tar.gz: 74e37de3b51ccdf102b335df496d17f189842004
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4bb14012a4409e313b68a895ac7d66f94d6b7ba8d05d36922daa3ecffaafc5f82d318030fae3fc38e39f1f034755443e62334fc458537e8015426e1ffa201c4c
|
7
|
+
data.tar.gz: 91f86a3e97164de1d57306a2eb5bc15015dd16fb1eba08b41b42428ced1ef0540fe0b2c734a84dc1f42824fe867d4da88a56cea01745f9678b09d58d4de61d32
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
Tzispa
|
2
|
+
|
3
|
+
General purpose web framework
|
4
|
+
|
5
|
+
## v0.3.3
|
6
|
+
- Added internationalization support with i18n
|
7
|
+
- Created new config folder in each domain to store all configurations
|
8
|
+
- Moved webconfig.yml to the config folder
|
9
|
+
- New routes definition in-app instead of yml config file
|
10
|
+
- Added framework routes 'api', 'site' and 'index' in Routes class methods
|
11
|
+
|
12
|
+
## v0.3.2
|
13
|
+
- Added 'use' method in Repository to support multiple adapters in apps
|
14
|
+
- new repository management to allow 'local' and gem repositories
|
15
|
+
- repository dup in context to separate repository state between requests
|
16
|
+
- log unrescued errors in base controller
|
17
|
+
- raise custom exceptions to notify unknown models, adapters
|
18
|
+
|
19
|
+
## v0.3.1
|
20
|
+
- Moved model to entity monkey patched methods to independent module 'Entity'
|
21
|
+
- Preload all repository model classes in application startup
|
22
|
+
|
23
|
+
## v0.3.0
|
24
|
+
- Added Rig templates caching in Application
|
25
|
+
- Added mutex sync access to the shared repository
|
26
|
+
|
27
|
+
## v0.2.9
|
28
|
+
- Move Rig template engine into independent gem: tzispa_rig
|
29
|
+
|
30
|
+
## v0.2.8
|
31
|
+
- Move constantize and camelize functions into class Tzispa::Utils::String
|
32
|
+
- Move Decorator class into tzispa_utils
|
33
|
+
|
34
|
+
## v0.2.7
|
35
|
+
- Split helpers modules into independent gem: tzispa_helpers
|
36
|
+
|
37
|
+
## v0.2.6
|
38
|
+
- Added mail helper
|
39
|
+
|
40
|
+
## v0.2.5
|
41
|
+
- Split utilities into another gem: tzispa_utils
|
42
|
+
|
43
|
+
## v0.2.4
|
44
|
+
- Added Repository / Model / Entity abstraction layers
|
45
|
+
|
46
|
+
## v0.2.0
|
47
|
+
- Removed Lotus dependencies and implementing a mininal http core framework based on Rack
|
48
|
+
|
49
|
+
## v0.1.1
|
50
|
+
- Added basic configuration api
|
51
|
+
- Added Model::Base class
|
52
|
+
- Added Configuration by convention to easy manage Rig Engine parameters
|
53
|
+
|
54
|
+
## v0.1.0
|
55
|
+
- Library implemented as a gem, previously only a bunch of files
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Tzispa
|
6
|
+
module Api
|
7
|
+
class Handler
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :@context, :request, :response, :app
|
11
|
+
|
12
|
+
attr_reader :context, :status, :response_verb, :data
|
13
|
+
|
14
|
+
HANDLED_UNDEFINED = nil
|
15
|
+
HANDLED_OK = 1
|
16
|
+
HANDLED_MISSING_PARAMETER = 98
|
17
|
+
HANDLED_ERROR = 99
|
18
|
+
HANDLED_RESULT = 200
|
19
|
+
|
20
|
+
HANDLED_MESSAGES = {
|
21
|
+
HANDLED_OK => 'La operación se ha realizado correctamente',
|
22
|
+
HANDLED_MISSING_PARAMETER => 'Error: faltan parámetros para realizar la operación',
|
23
|
+
HANDLED_ERROR => 'Error indeterminado: la operación no se ha podido realizar'
|
24
|
+
}
|
25
|
+
|
26
|
+
def initialize(context)
|
27
|
+
@context = context
|
28
|
+
end
|
29
|
+
|
30
|
+
def result(response_verb:, status: HANDLED_UNDEFINED, data: nil, detailed_error: nil)
|
31
|
+
@status = status
|
32
|
+
@response_verb = response_verb
|
33
|
+
@data = data
|
34
|
+
@detailed_error = detailed_error
|
35
|
+
end
|
36
|
+
|
37
|
+
def message
|
38
|
+
if @status.nil?
|
39
|
+
nil
|
40
|
+
elsif @status >= HANDLED_OK && @status <= HANDLED_ERROR
|
41
|
+
HANDLED_MESSAGES[@status]
|
42
|
+
elsif @status > HANDLED_ERROR && @status < HANDLED_RESULT
|
43
|
+
error_message @status
|
44
|
+
elsif @status > HANDLED_RESULT
|
45
|
+
result_messages @status
|
46
|
+
else
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def result_messages(status)
|
54
|
+
self.class::RESULT_MESSAGES[status] if (defined?( self.class::RESULT_MESSAGES ) && self.class::RESULT_MESSAGES.is_a?(Hash))
|
55
|
+
end
|
56
|
+
|
57
|
+
def error_message(status)
|
58
|
+
"#{self.class::ERROR_MESSAGES[status]}#{': '+@detailed_error if @detailed_error}" if (defined?( self.class::ERROR_MESSAGES ) && self.class::ERROR_MESSAGES.is_a?(Hash))
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/tzispa/app.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'logger'
|
5
|
+
require 'i18n'
|
6
|
+
require 'tzispa/domain'
|
7
|
+
require 'tzispa/config/webconfig'
|
8
|
+
require 'tzispa/config/routes'
|
9
|
+
require 'tzispa/middleware'
|
10
|
+
require 'tzispa_data'
|
11
|
+
require "tzispa_rig"
|
12
|
+
|
13
|
+
|
14
|
+
module Tzispa
|
15
|
+
class Application
|
16
|
+
extend Forwardable
|
17
|
+
|
18
|
+
attr_reader :domain, :config, :middleware, :repository, :engine, :logger
|
19
|
+
def_delegator :@middleware, :use
|
20
|
+
def_delegator :@domain, :name
|
21
|
+
|
22
|
+
|
23
|
+
class << self
|
24
|
+
|
25
|
+
attr_accessor :routes
|
26
|
+
|
27
|
+
def inherited(base)
|
28
|
+
super
|
29
|
+
base.class_eval do
|
30
|
+
synchronize do
|
31
|
+
applications.add(base)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def applications
|
37
|
+
synchronize do
|
38
|
+
@@applications ||= Set.new
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def synchronize
|
43
|
+
Mutex.new.synchronize {
|
44
|
+
yield
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def mount(mount_point, builder)
|
49
|
+
self.routes ||= Tzispa::Config::Routes.new(mount_point)
|
50
|
+
app = self.new
|
51
|
+
yield(routes)
|
52
|
+
app.middleware.map mount_point, builder
|
53
|
+
end
|
54
|
+
|
55
|
+
def router
|
56
|
+
self.routes&.router
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize(domain_name)
|
62
|
+
@domain = Domain.new(name: domain_name)
|
63
|
+
@middleware = Tzispa::Middleware.new self
|
64
|
+
I18n.load_path = Dir["config/locales/*.yml"]
|
65
|
+
end
|
66
|
+
|
67
|
+
def call(env)
|
68
|
+
env[:tzispa__app] = self
|
69
|
+
middleware.call(env)
|
70
|
+
end
|
71
|
+
|
72
|
+
def load!
|
73
|
+
unless @loaded
|
74
|
+
Mutex.new.synchronize {
|
75
|
+
@config = Tzispa::Config::WebConfig.new(@domain).load!
|
76
|
+
@middleware.load!
|
77
|
+
@repository = Tzispa::Data::Repository.new(@config.repository.to_h).load! if @config.respond_to? :repository
|
78
|
+
@engine = Tzispa::Rig::Engine.new self
|
79
|
+
@logger = Logger.new("logs/#{@domain.name}.log", 'weekly')
|
80
|
+
@logger.level = @config.respond_to?(:developing) && @config.developing ? Logger::DEBUG : Logger::INFO
|
81
|
+
I18n.load_path += Dir["#{@domain.path}/config/locales/*.yml"] if @config.respond_to?(:locales) && @config.locales.preload
|
82
|
+
I18n.locale = @config.locales.default.to_sym if @config.respond_to?(:locales) && @config.locales.default
|
83
|
+
@loaded = true
|
84
|
+
}
|
85
|
+
end
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Tzispa
|
4
|
+
module Config
|
5
|
+
class Base < Struct
|
6
|
+
|
7
|
+
def self.parametrize(params)
|
8
|
+
self.new( *(params.keys.map { |k| k.to_sym })).new(*(params.values.map { |v|
|
9
|
+
v.is_a?(Hash) ? self.parametrize(v) : v
|
10
|
+
}))
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'http_router'
|
5
|
+
require 'tzispa/utils/string'
|
6
|
+
|
7
|
+
module Tzispa
|
8
|
+
module Config
|
9
|
+
class Routes
|
10
|
+
|
11
|
+
CONTROLLERS_BASE = 'Tzispa::Controller'
|
12
|
+
|
13
|
+
attr_reader :router, :map_path
|
14
|
+
|
15
|
+
def initialize(map_path=nil)
|
16
|
+
@router = HttpRouter.new
|
17
|
+
@map_path = map_path unless map_path=='/'
|
18
|
+
end
|
19
|
+
|
20
|
+
def path(path_id, params={})
|
21
|
+
"#{@map_path}#{@router.path path_id, params}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def add(route_id, path, controller)
|
25
|
+
spec_control, callmethod = controller.to_s.split(':')
|
26
|
+
mpath = spec_control.split('#')
|
27
|
+
controller = TzString.camelize(mpath.pop).to_s
|
28
|
+
if mpath.count > 1
|
29
|
+
controller_module = mpath.collect!{ |w| w.capitalize }.join('::')
|
30
|
+
require_relative "./controller/#{controller.downcase}"
|
31
|
+
else
|
32
|
+
controller_module = CONTROLLERS_BASE
|
33
|
+
require "tzispa/controller/#{controller.downcase}"
|
34
|
+
end
|
35
|
+
@router.add(path).tap { |rule|
|
36
|
+
rule.to TzString.constantize("#{controller_module}::#{controller}").new(callmethod)
|
37
|
+
rule.name = route_id
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def index(path, controller=nil)
|
42
|
+
add :index, path, controller || 'layout:render!'
|
43
|
+
end
|
44
|
+
|
45
|
+
def api(path, controller=nil)
|
46
|
+
add :api, path, controller || 'api:dispatch!'
|
47
|
+
end
|
48
|
+
|
49
|
+
def site(path, controller=nil)
|
50
|
+
add :site, path, controller || 'layout:render!'
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tzispa/config/yaml'
|
4
|
+
|
5
|
+
module Tzispa
|
6
|
+
module Config
|
7
|
+
class WebConfig
|
8
|
+
|
9
|
+
RPG_WEBCONFIG_FILENAME = :webconfig
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(domain,configname=nil)
|
13
|
+
@domain = domain
|
14
|
+
@cfname = configname || RPG_WEBCONFIG_FILENAME
|
15
|
+
@cftime = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def filename
|
19
|
+
@filename ||= "#{@domain.path}/config/#{@cfname}.yml".freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
def load!
|
23
|
+
if @cftime.nil?
|
24
|
+
@cftime = File.ctime(filename)
|
25
|
+
else
|
26
|
+
if @cftime != File.ctime(filename)
|
27
|
+
@config = nil
|
28
|
+
@cftime = File.ctime(filename)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@config ||= Tzispa::Config::Yaml.load(filename)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'tzispa/config/base'
|
3
|
+
|
4
|
+
module Tzispa
|
5
|
+
module Config
|
6
|
+
class Yaml < Tzispa::Config::Base
|
7
|
+
|
8
|
+
|
9
|
+
def self.load(filename)
|
10
|
+
params = YAML.load(File.open(filename))
|
11
|
+
self.parametrize params
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'tzispa/domain'
|
5
|
+
require 'tzispa/controller/base'
|
6
|
+
require 'tzispa/controller/exceptions'
|
7
|
+
require 'tzispa/helpers/security'
|
8
|
+
require 'tzispa/helpers/response'
|
9
|
+
require 'tzispa/utils/string'
|
10
|
+
|
11
|
+
|
12
|
+
module Tzispa
|
13
|
+
module Controller
|
14
|
+
class Api < Base
|
15
|
+
|
16
|
+
include Tzispa::Helpers::Security
|
17
|
+
include Tzispa::Helpers::Response
|
18
|
+
|
19
|
+
def dispatch!
|
20
|
+
raise Error::InvalidSign.new unless sign?
|
21
|
+
@handler, domain_name = context.router_params[:handler].split('.').reverse
|
22
|
+
@domain = domain_name.nil? ? context.app.domain : Tzispa::Domain.new(name: domain_name)
|
23
|
+
@verb = context.router_params[:verb]
|
24
|
+
@predicate = context.router_params[:predicate]
|
25
|
+
@hnd = handler_class.new(context)
|
26
|
+
@predicate ? hnd.send(@verb, @predicate) : hnd.send(@verb)
|
27
|
+
send hnd.response_verb
|
28
|
+
response.finish
|
29
|
+
end
|
30
|
+
|
31
|
+
def redirect
|
32
|
+
context.flash << hnd.message
|
33
|
+
url = hnd.data
|
34
|
+
context.redirect url, response
|
35
|
+
end
|
36
|
+
|
37
|
+
def html
|
38
|
+
context.flash << hnd.message
|
39
|
+
response.body << hnd.data
|
40
|
+
content_type :htm
|
41
|
+
set_action_headers
|
42
|
+
end
|
43
|
+
|
44
|
+
def json
|
45
|
+
data = hnd.data.is_a?(::Hash) ? hnd.data : JSON.parse(hnd.data)
|
46
|
+
data[:__result_status] = hnd.status
|
47
|
+
data[:__result_message] = hnd.message
|
48
|
+
response.body << data.to_json.to_s
|
49
|
+
content_type :json
|
50
|
+
set_action_headers
|
51
|
+
end
|
52
|
+
|
53
|
+
def text
|
54
|
+
context.flash << hnd.message
|
55
|
+
response.body << hnd.data
|
56
|
+
content_type :text
|
57
|
+
set_action_headers
|
58
|
+
end
|
59
|
+
|
60
|
+
def download
|
61
|
+
context.flash << hnd.message
|
62
|
+
data = hnd.data
|
63
|
+
path = "#{Dir.pwd}/#{data[:path]}"
|
64
|
+
send_file path, data
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
attr_reader :hnd
|
70
|
+
|
71
|
+
def set_action_headers
|
72
|
+
response['X-API'] = "#{context.router_params[:sign]}:#{context.router_params[:handler]}:#{context.router_params[:verb]}:#{context.router_params[:predicate]}"
|
73
|
+
response['X-API-STATE'] = "#{hnd.status}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def sign?
|
77
|
+
context.router_params[:sign] == sign_array([
|
78
|
+
context.router_params[:handler],
|
79
|
+
context.router_params[:verb],
|
80
|
+
context.router_params[:predicate]
|
81
|
+
],
|
82
|
+
context.app.config.salt)
|
83
|
+
end
|
84
|
+
|
85
|
+
def handler_class_name
|
86
|
+
"#{TzString.camelize @domain.name }::Api::#{TzString.camelize @handler }Handler"
|
87
|
+
end
|
88
|
+
|
89
|
+
def handler_class
|
90
|
+
@domain.require "api/#{@handler.downcase}"
|
91
|
+
TzString.constantize handler_class_name
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'tzispa/version'
|
6
|
+
require 'tzispa/http/context'
|
7
|
+
require 'tzispa/rig/template'
|
8
|
+
|
9
|
+
|
10
|
+
module Tzispa
|
11
|
+
module Controller
|
12
|
+
|
13
|
+
class Base
|
14
|
+
extend Forwardable
|
15
|
+
|
16
|
+
attr_reader :context
|
17
|
+
def_delegators :@context, :request, :response, :config
|
18
|
+
|
19
|
+
def initialize(callmethod)
|
20
|
+
@callmethod = callmethod
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(environment)
|
24
|
+
@context = Tzispa::Http::Context.new(environment)
|
25
|
+
invoke @callmethod
|
26
|
+
response.finish
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def invoke(callmethod)
|
32
|
+
status = catch(:halt) {
|
33
|
+
begin
|
34
|
+
send "#{@callmethod}"
|
35
|
+
rescue StandardError, ScriptError => ex
|
36
|
+
context.app.logger.error "#{ex.message}\n#{ex.backtrace.map { |trace| "\t #{trace}" }.join('\n') if ex.respond_to?(:backtrace) && ex.backtrace}"
|
37
|
+
error error_report(ex)
|
38
|
+
end
|
39
|
+
}
|
40
|
+
response.status = status if status.is_a?(Integer)
|
41
|
+
error_page(response.status) if (response.client_error? || response.server_error?) && !config.developing
|
42
|
+
end
|
43
|
+
|
44
|
+
def error(body)
|
45
|
+
500.tap { |code|
|
46
|
+
response.status = code
|
47
|
+
response.body = body
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def error_report(error=nil)
|
52
|
+
text = String.new('<!DOCTYPE html>')
|
53
|
+
text << '<html lang="es"><head>'
|
54
|
+
text << '<meta charset="utf-8" />'
|
55
|
+
text << '<style> html {background:#cccccc; font-family:Arial; font-size:15px; color:#555;} body {width:75%; max-width:1200px; margin:18px auto; background:#fff; border-radius:6px; padding:32px 24px;} ul{list-style:none; margin:0; padding:0;} li{font-style:italic; color:#666;} h1 {color:#2ECC71;} </style>'
|
56
|
+
text << '</head><body>'
|
57
|
+
text << "<h5>#{Tzispa::FRAMEWORK_NAME} #{Tzispa::VERSION}</h5>\n"
|
58
|
+
if error && config.developing
|
59
|
+
text << "<h1>#{error.class.name}</h1><h3>#{error.message}</h1>\n"
|
60
|
+
text << '<ol>' + error.backtrace.map { |trace| "<li>#{trace}</li>\n" }.join + '</ol>' if error.respond_to?(:backtrace) && error.backtrace
|
61
|
+
else
|
62
|
+
text << "<h1>Error 500</h1>\n"
|
63
|
+
text << "Se ha producido un error inesperado al tramitar la petición"
|
64
|
+
end
|
65
|
+
text << '</body></html>'
|
66
|
+
end
|
67
|
+
|
68
|
+
def error_page(status)
|
69
|
+
begin
|
70
|
+
error_file = "#{@app.domain.path}/error/#{status}.htm"
|
71
|
+
response.body = Tzispa::Rig::File.new(error_file).load!.content
|
72
|
+
rescue
|
73
|
+
response.body = String.new('<!DOCTYPE html>')
|
74
|
+
response.body << '<html lang="es"><head>'
|
75
|
+
response.body << '<meta charset="utf-8" />'
|
76
|
+
response.body << '<style> html {background:#cccccc; font-family:Arial; font-size:15px; color:#555;} body {width:75%; max-width:1200px; margin:18px auto; background:#fff; border-radius:6px; padding:32px 24px;} #main {margin:auto; } h1 {color:#2ECC71; font-size:4em; text-align:center;} </style>'
|
77
|
+
response.body << '</head><body>'
|
78
|
+
response.body << '<div id="main">'
|
79
|
+
response.body << "<h5>#{Tzispa::FRAMEWORK_NAME} #{Tzispa::VERSION}</h5>\n"
|
80
|
+
response.body << "<h1>Error #{status}</h1>\n"
|
81
|
+
response.body << '</div>'
|
82
|
+
response.body << '</body></html>'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tzispa_rig'
|
4
|
+
require 'tzispa/controller/base'
|
5
|
+
require 'tzispa/controller/exceptions'
|
6
|
+
require 'tzispa/helpers/response'
|
7
|
+
|
8
|
+
module Tzispa
|
9
|
+
module Controller
|
10
|
+
class Layout < Base
|
11
|
+
|
12
|
+
include Tzispa::Helpers::Response
|
13
|
+
|
14
|
+
def render!
|
15
|
+
layout = if context.config.auth_required && !context.logged? && context.router_params[:layout]
|
16
|
+
context.config.default_layout
|
17
|
+
else
|
18
|
+
context.router_params[:layout] || context.config.default_layout
|
19
|
+
end
|
20
|
+
layout_format = context.router_params[:format] || context.config.default_format
|
21
|
+
rig = context.app.engine.layout(name: layout, format: layout_format.to_sym)
|
22
|
+
response.body << rig.render(context)
|
23
|
+
content_type layout_format
|
24
|
+
set_layout_headers
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def set_layout_headers
|
30
|
+
headers = Hash.new
|
31
|
+
if context.app.config.cache.layout.enabled
|
32
|
+
headers['Cache-Control'] = context.app.config.cache.layout.control
|
33
|
+
if context.app.config.cache.layout.expires
|
34
|
+
headers['Expires'] = (Time.now + context.app.config.cache.layout.expires).utc.rfc2822
|
35
|
+
end
|
36
|
+
end
|
37
|
+
response.headers.merge!(headers)
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tzispa/utils/string'
|
4
|
+
|
5
|
+
module Tzispa
|
6
|
+
module Data
|
7
|
+
module Entity
|
8
|
+
|
9
|
+
def self.included(base)
|
10
|
+
base.extend(ClassMethods)
|
11
|
+
end
|
12
|
+
|
13
|
+
def entity!
|
14
|
+
@__entity || @__entity = self.class.entity_class.new(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def entity_class
|
19
|
+
class_variable_defined?(:@@__entity_class) ?
|
20
|
+
class_variable_get(:@@__entity_class) :
|
21
|
+
class_variable_set(:@@__entity_class, TzString.constantize("#{self}Entity") )
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
#unless model_class.respond_to?(:entity_class!)
|
26
|
+
# model_class.send(:define_singleton_method, :entity_class) {
|
27
|
+
# class_variable_defined?(:@@__entity_class) ?
|
28
|
+
# class_variable_get(:@@__entity_class) :
|
29
|
+
# class_variable_set(:@@__entity_class, TzString.constantize("#{self}Entity") )
|
30
|
+
# }
|
31
|
+
#end
|
32
|
+
#model_class.send(:define_method, :entity!) {
|
33
|
+
# instance_variable_defined?(:@__entity) ?
|
34
|
+
# instance_variable_get(:@__entity) :
|
35
|
+
# instance_variable_set(:@__entity, self.class.entity_class!.new(self))
|
36
|
+
#}
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tzispa
|
4
|
+
class Domain
|
5
|
+
|
6
|
+
attr_reader :name, :root
|
7
|
+
|
8
|
+
DEFAULT_DOMAIN_NAME = :default
|
9
|
+
DEFAULT_DOMAINS_ROOT = :domains
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(name: DEFAULT_DOMAIN_NAME, root: DEFAULT_DOMAINS_ROOT)
|
13
|
+
@name = name || DEFAULT_DOMAIN_NAME
|
14
|
+
@root = root || DEFAULT_DOMAINS_ROOT
|
15
|
+
end
|
16
|
+
|
17
|
+
def path
|
18
|
+
"#{root.to_s.downcase}/#{name.to_s.downcase}".freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
def require(file)
|
22
|
+
Kernel.require "./#{path}/#{file}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def load(file)
|
26
|
+
Kernel.load "./#{path}/#{file}.rb"
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.require(domain, file)
|
30
|
+
self.new(name: domain).require(file)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.load(domain, file)
|
34
|
+
self.new(name: domain).load(file)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'time'
|
3
|
+
require 'tzispa/http/response'
|
4
|
+
require 'tzispa/http/request'
|
5
|
+
require 'tzispa/http/session_flash_bag'
|
6
|
+
require 'tzispa/helpers/response'
|
7
|
+
require 'tzispa/helpers/security'
|
8
|
+
|
9
|
+
module Tzispa
|
10
|
+
module Http
|
11
|
+
|
12
|
+
class Context
|
13
|
+
extend Forwardable
|
14
|
+
|
15
|
+
include Tzispa::Helpers::Response
|
16
|
+
include Tzispa::Helpers::Security
|
17
|
+
|
18
|
+
attr_reader :app, :env, :request, :response, :repository
|
19
|
+
attr_accessor :domain
|
20
|
+
def_delegators :@request, :session
|
21
|
+
def_delegators :@app, :config, :logger
|
22
|
+
|
23
|
+
SESSION_LAST_ACCESS = :__last_access
|
24
|
+
SESSION_AUTH_USER = :__auth__user
|
25
|
+
GLOBAL_MESSAGE_FLASH = :__global_message_flash
|
26
|
+
|
27
|
+
|
28
|
+
def initialize(environment)
|
29
|
+
@env = environment
|
30
|
+
@app = environment[:tzispa__app]
|
31
|
+
@request = Tzispa::Http::Request.new(environment)
|
32
|
+
@response = Tzispa::Http::Response.new
|
33
|
+
@repository = @app.repository.dup if @app.repository
|
34
|
+
#set_last_access if config.sessions.enabled
|
35
|
+
end
|
36
|
+
|
37
|
+
def router_params
|
38
|
+
@env['router.params']
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_last_access
|
42
|
+
session[SESSION_LAST_ACCESS] = Time.now.utc.iso8601
|
43
|
+
end
|
44
|
+
|
45
|
+
def last_access
|
46
|
+
session[SESSION_LAST_ACCESS]
|
47
|
+
end
|
48
|
+
|
49
|
+
def flash
|
50
|
+
SessionFlashBag.new(session, GLOBAL_MESSAGE_FLASH)
|
51
|
+
end
|
52
|
+
|
53
|
+
def logged?
|
54
|
+
not session[SESSION_AUTH_USER].nil?
|
55
|
+
end
|
56
|
+
|
57
|
+
def login=(user)
|
58
|
+
session[SESSION_AUTH_USER] = user if not user.nil?
|
59
|
+
end
|
60
|
+
|
61
|
+
def login
|
62
|
+
session[SESSION_AUTH_USER]
|
63
|
+
end
|
64
|
+
|
65
|
+
def logout
|
66
|
+
session.delete(SESSION_AUTH_USER)
|
67
|
+
end
|
68
|
+
|
69
|
+
def path(path_id, params={})
|
70
|
+
@app.class.routes.path path_id, params
|
71
|
+
end
|
72
|
+
|
73
|
+
def api(handler, verb, predicate, sufix)
|
74
|
+
raise ArgumentError.new('missing parameter in api call') unless handler && verb
|
75
|
+
sign = sign_array [handler, verb, predicate], @app.config.salt
|
76
|
+
@app.class.routes.path :api, {sign: sign, handler: handler, verb: verb, predicate: predicate, sufix: sufix}
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
|
5
|
+
module Tzispa
|
6
|
+
module Http
|
7
|
+
class Request < Rack::Request
|
8
|
+
|
9
|
+
alias secure? ssl?
|
10
|
+
|
11
|
+
def forwarded?
|
12
|
+
@env.include? "HTTP_X_FORWARDED_HOST"
|
13
|
+
end
|
14
|
+
|
15
|
+
def safe?
|
16
|
+
get? or head? or options? or trace?
|
17
|
+
end
|
18
|
+
|
19
|
+
def idempotent?
|
20
|
+
safe? or put? or delete? or link? or unlink?
|
21
|
+
end
|
22
|
+
|
23
|
+
def link?
|
24
|
+
request_method == "LINK"
|
25
|
+
end
|
26
|
+
|
27
|
+
def unlink?
|
28
|
+
request_method == "UNLINK"
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
|
5
|
+
module Tzispa
|
6
|
+
module Http
|
7
|
+
|
8
|
+
class Response < Rack::Response
|
9
|
+
|
10
|
+
DROP_BODY_RESPONSES = [204, 205, 304]
|
11
|
+
|
12
|
+
def initialize(*)
|
13
|
+
super
|
14
|
+
headers['Content-Type'] ||= 'text/html'
|
15
|
+
end
|
16
|
+
|
17
|
+
def body=(value)
|
18
|
+
value = value.body while Rack::Response === value
|
19
|
+
@body = String === value ? [value.to_str] : value
|
20
|
+
end
|
21
|
+
|
22
|
+
def each
|
23
|
+
block_given? ? super : enum_for(:each)
|
24
|
+
end
|
25
|
+
|
26
|
+
def finish
|
27
|
+
result = body
|
28
|
+
|
29
|
+
if drop_content_info?
|
30
|
+
headers.delete "Content-Length"
|
31
|
+
headers.delete "Content-Type"
|
32
|
+
end
|
33
|
+
|
34
|
+
if drop_body?
|
35
|
+
close
|
36
|
+
result = []
|
37
|
+
end
|
38
|
+
|
39
|
+
if calculate_content_length?
|
40
|
+
# if some other code has already set Content-Length, don't muck with it
|
41
|
+
# currently, this would be the static file-handler
|
42
|
+
headers["Content-Length"] = body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
|
43
|
+
end
|
44
|
+
headers['X-Powered-By'] = "#{Tzispa::FRAMEWORK_NAME}"
|
45
|
+
[status.to_i, headers, result]
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def calculate_content_length?
|
51
|
+
headers["Content-Type"] and not headers["Content-Length"] and Array === body
|
52
|
+
end
|
53
|
+
|
54
|
+
def drop_content_info?
|
55
|
+
status.to_i / 100 == 1 or drop_body?
|
56
|
+
end
|
57
|
+
|
58
|
+
def drop_body?
|
59
|
+
DROP_BODY_RESPONSES.include?(status.to_i)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Tzispa
|
6
|
+
module Http
|
7
|
+
|
8
|
+
class SessionFlashBag
|
9
|
+
|
10
|
+
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
def_delegators :@bag, :count, :length, :size, :each
|
14
|
+
|
15
|
+
SESSION_FLASH_BAG = :__flash_bag
|
16
|
+
|
17
|
+
def initialize(session, key)
|
18
|
+
@session = session
|
19
|
+
@session_key = "#{SESSION_FLASH_BAG}_#{key}".to_sym
|
20
|
+
load!
|
21
|
+
end
|
22
|
+
|
23
|
+
def << (value)
|
24
|
+
if not value.nil?
|
25
|
+
@bag << value
|
26
|
+
store
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def pop
|
31
|
+
value = @bag.pop
|
32
|
+
store
|
33
|
+
value
|
34
|
+
end
|
35
|
+
|
36
|
+
def pop_all
|
37
|
+
empty!
|
38
|
+
@bag
|
39
|
+
end
|
40
|
+
|
41
|
+
def push(value)
|
42
|
+
@bag.push value
|
43
|
+
store
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def load!
|
49
|
+
@bag = @session[@session_key] ? Marshal.load(@session[@session_key]) : Array.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def store
|
53
|
+
@session[@session_key] = Marshal.dump @bag
|
54
|
+
end
|
55
|
+
|
56
|
+
def empty!
|
57
|
+
@session[@session_key] = Array.new
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'moneta'
|
4
|
+
require 'securerandom'
|
5
|
+
require 'rack/session/moneta'
|
6
|
+
|
7
|
+
module Tzispa
|
8
|
+
class Middleware
|
9
|
+
|
10
|
+
|
11
|
+
def initialize(app)
|
12
|
+
@stack = []
|
13
|
+
@application = app
|
14
|
+
end
|
15
|
+
|
16
|
+
def load!
|
17
|
+
@builder = ::Rack::Builder.new
|
18
|
+
load_default_stack
|
19
|
+
@stack.each { |m, args, block| @builder.use load_middleware(m), *args, &block }
|
20
|
+
@builder.run @application.class.router
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def map(mount_path, builder)
|
25
|
+
app = @application
|
26
|
+
builder.map mount_path do
|
27
|
+
run app.load!
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def call(env)
|
32
|
+
@builder.call(env)
|
33
|
+
end
|
34
|
+
|
35
|
+
def use(middleware, *args, &blk)
|
36
|
+
@stack.unshift [middleware, args, blk]
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_middleware(middleware)
|
40
|
+
case middleware
|
41
|
+
when String
|
42
|
+
@application.domain.const_get(middleware)
|
43
|
+
else
|
44
|
+
middleware
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def load_default_stack
|
49
|
+
@default_stack_loaded ||= begin
|
50
|
+
_load_session_middleware
|
51
|
+
_load_asset_middlewares
|
52
|
+
use Rack::MethodOverride
|
53
|
+
true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def _load_session_middleware
|
58
|
+
if @application.config.sessions.enabled
|
59
|
+
use Rack::Session::Moneta,
|
60
|
+
store: Moneta.new(:HashFile, dir: './data/session', expires: true, threadsafe: true),
|
61
|
+
key: "_#{@application.config.id}__", ##{SecureRandom.hex(18)}
|
62
|
+
domain: @application.config.host_name,
|
63
|
+
path: '/',
|
64
|
+
expire_after: @application.config.sessions.timeout,
|
65
|
+
secret: @application.config.sessions.secret
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def _load_asset_middlewares
|
70
|
+
use Rack::Static,
|
71
|
+
:urls => ["/img", "/js", "/css", "/*.ico"],
|
72
|
+
:root => "public",
|
73
|
+
:header_rules => [
|
74
|
+
[:all, {'Cache-Control' => 'public, max-age=72000'}],
|
75
|
+
['css', {'Content-Type' => 'text/css; charset=utf-8'}],
|
76
|
+
['js', {'Content-Type' => 'text/javascript; charset=utf-8'}]
|
77
|
+
]
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
data/lib/tzispa.rb
ADDED
metadata
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tzispa
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Juan Antonio Piñero
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-02-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: http_router
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.11'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.11'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: moneta
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.8'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: tzispa_helpers
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.1.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.1.0
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: tzispa_utils
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.1.2
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.1.2
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: tzispa_rig
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.2.0
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.2.0
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: tzispa_data
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.1'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.1'
|
111
|
+
description: A sparkling web framework based on Rack and inspired by Sinatra and Lotus
|
112
|
+
email:
|
113
|
+
- japinero@area-integral.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- CHANGELOG.md
|
119
|
+
- README.md
|
120
|
+
- lib/tzispa.rb
|
121
|
+
- lib/tzispa/api/handler.rb
|
122
|
+
- lib/tzispa/app.rb
|
123
|
+
- lib/tzispa/config/base.rb
|
124
|
+
- lib/tzispa/config/routes.rb
|
125
|
+
- lib/tzispa/config/webconfig.rb
|
126
|
+
- lib/tzispa/config/yaml.rb
|
127
|
+
- lib/tzispa/controller/api.rb
|
128
|
+
- lib/tzispa/controller/base.rb
|
129
|
+
- lib/tzispa/controller/exceptions.rb
|
130
|
+
- lib/tzispa/controller/layout.rb
|
131
|
+
- lib/tzispa/data/entity.rb
|
132
|
+
- lib/tzispa/domain.rb
|
133
|
+
- lib/tzispa/http/context.rb
|
134
|
+
- lib/tzispa/http/request.rb
|
135
|
+
- lib/tzispa/http/response.rb
|
136
|
+
- lib/tzispa/http/session_flash_bag.rb
|
137
|
+
- lib/tzispa/middleware.rb
|
138
|
+
- lib/tzispa/version.rb
|
139
|
+
homepage: https://www.area-integral.com
|
140
|
+
licenses:
|
141
|
+
- MIT
|
142
|
+
metadata: {}
|
143
|
+
post_install_message:
|
144
|
+
rdoc_options: []
|
145
|
+
require_paths:
|
146
|
+
- lib
|
147
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - "~>"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '2.0'
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - "~>"
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '2.0'
|
157
|
+
requirements: []
|
158
|
+
rubyforge_project:
|
159
|
+
rubygems_version: 2.5.1
|
160
|
+
signing_key:
|
161
|
+
specification_version: 4
|
162
|
+
summary: A sparkling web framework
|
163
|
+
test_files: []
|