startback 0.3.2 → 0.4.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.
- checksums.yaml +4 -4
- data/lib/startback.rb +1 -1
- data/lib/startback/support.rb +7 -0
- data/lib/startback/version.rb +2 -2
- data/lib/startback/web/auto_caching.rb +85 -0
- data/lib/startback/web/cors_headers.rb +80 -0
- data/lib/startback/web/magic_assets.rb +94 -0
- data/lib/startback/web/magic_assets/rake_tasks.rb +64 -0
- data/spec/unit/test_support.rb +32 -0
- data/spec/unit/web/fixtures/assets/app/hello.es6 +4 -0
- data/spec/unit/web/fixtures/assets/index.es6 +1 -0
- data/spec/unit/web/test_auto_caching.rb +67 -0
- data/spec/unit/web/test_catch_all.rb +1 -0
- data/spec/unit/web/test_cors_headers.rb +88 -0
- data/spec/unit/web/test_healthcheck.rb +1 -0
- data/spec/unit/web/test_magic_assets.rb +61 -0
- metadata +55 -19
- data/lib/startback/web.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c343f2a58bb989ce6d4f14f0fe478a0e8b69ac8
|
4
|
+
data.tar.gz: 4ef6a2962120e23b5f4afdd401918a5c8688ac58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f3ac41ca8f1bd8ea51ef4d58d868aa0694839ccbbc372a1fa0445c5f20be02c3ff036fed65cbdc24aa0283b37256b54aa16aa5711019891a648bb8c0fab34a3
|
7
|
+
data.tar.gz: db4057a610fd4e8676816bd05892419900f860dbc4b622ca2d34d93702d1a50418d0a43f56105ac1fbd2a2fc46d73d7974aed6d1ffedcab41238c0f5ef40c526
|
data/lib/startback.rb
CHANGED
@@ -2,6 +2,7 @@ require 'sinatra'
|
|
2
2
|
require 'rack/robustness'
|
3
3
|
require 'finitio'
|
4
4
|
require 'logger'
|
5
|
+
require 'path'
|
5
6
|
# Provides a reusable backend framework for backend components written
|
6
7
|
# in ruby.
|
7
8
|
#
|
@@ -29,7 +30,6 @@ module Startback
|
|
29
30
|
require_relative 'startback/support'
|
30
31
|
require_relative 'startback/context'
|
31
32
|
require_relative 'startback/operation'
|
32
|
-
require_relative 'startback/web'
|
33
33
|
|
34
34
|
# Logger instance to use for the application
|
35
35
|
LOGGER = ::Startback::Support::Logger.new
|
data/lib/startback/support.rb
CHANGED
@@ -5,6 +5,13 @@ module Startback
|
|
5
5
|
Startback::LOGGER
|
6
6
|
end
|
7
7
|
|
8
|
+
def deep_merge(h1, h2)
|
9
|
+
h1.merge(h2){|k,v1,v2|
|
10
|
+
v1.is_a?(Hash) && v2.is_a?(Hash) ? deep_merge(v1, v2) : v2
|
11
|
+
}
|
12
|
+
end
|
13
|
+
module_function :deep_merge
|
14
|
+
|
8
15
|
end # module Support
|
9
16
|
end # module Startback
|
10
17
|
require_relative 'support/logger'
|
data/lib/startback/version.rb
CHANGED
@@ -0,0 +1,85 @@
|
|
1
|
+
module Startback
|
2
|
+
module Web
|
3
|
+
#
|
4
|
+
# This rack middleware automatically mark response as non being cacheable
|
5
|
+
# in development, and being cacheble in production.
|
6
|
+
#
|
7
|
+
# The headers to set in development and production can be passed at
|
8
|
+
# construction, as well as whether the development environment must be
|
9
|
+
# forced. This class may also be configured through environment variables:
|
10
|
+
#
|
11
|
+
# - RACK_ENV: when "production" use the production headers, otherwise use
|
12
|
+
# the development ones
|
13
|
+
# - STARTBACK_AUTOCACHING_DEVELOPMENT_CACHE_CONTROL: Cache-Control header
|
14
|
+
# to use in development mode
|
15
|
+
# - STARTBACK_AUTOCACHING_PRODUCTION_CACHE_CONTROL: Cache-Control header
|
16
|
+
# to use in production mode
|
17
|
+
#
|
18
|
+
# Example:
|
19
|
+
#
|
20
|
+
# # Default configuration
|
21
|
+
# use Autocaching
|
22
|
+
#
|
23
|
+
# # Force development mode
|
24
|
+
# use Autocaching, true
|
25
|
+
#
|
26
|
+
# # Force production mode
|
27
|
+
# use Autocaching, false
|
28
|
+
#
|
29
|
+
# # Set production headers manually
|
30
|
+
# use Autocaching, { :production => "public, no-cache, no-store" }
|
31
|
+
#
|
32
|
+
class AutoCaching
|
33
|
+
|
34
|
+
# Cache-Control header to use in development mode
|
35
|
+
DEVELOPMENT_CACHE_CONTROL = ENV['STARTBACK_AUTOCACHING_DEVELOPMENT_CACHE_CONTROL'] || \
|
36
|
+
"no-cache, no-store, max-age=0, must-revalidate"
|
37
|
+
|
38
|
+
# Cache-Control header to use in produdction mode
|
39
|
+
PRODUCTION_CACHE_CONTROL = ENV['STARTBACK_AUTOCACHING_PRODUCTION_CACHE_CONTROL'] ||\
|
40
|
+
"public, must-revalidate, max-age=3600, s-max-age=3600"
|
41
|
+
|
42
|
+
def initialize(app, development = nil, cache_headers = {})
|
43
|
+
development, cache_headers = nil, development if development.is_a?(Hash)
|
44
|
+
@app = app
|
45
|
+
@development = development.nil? ? infer_is_development : development
|
46
|
+
@cache_headers = default_headers.merge(normalize_headers(cache_headers))
|
47
|
+
end
|
48
|
+
|
49
|
+
def call(env)
|
50
|
+
status, headers, body = @app.call(env)
|
51
|
+
[status, patch_response_headers(headers), body]
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def patch_response_headers(hs)
|
57
|
+
hs.merge(development? ? @cache_headers[:development] : @cache_headers[:production])
|
58
|
+
end
|
59
|
+
|
60
|
+
def development?
|
61
|
+
!!@development
|
62
|
+
end
|
63
|
+
|
64
|
+
def infer_is_development
|
65
|
+
ENV['RACK_ENV'] != "production"
|
66
|
+
end
|
67
|
+
|
68
|
+
def default_headers
|
69
|
+
{
|
70
|
+
development: {
|
71
|
+
"Cache-Control" => DEVELOPMENT_CACHE_CONTROL
|
72
|
+
},
|
73
|
+
production: {
|
74
|
+
"Cache-Control" => PRODUCTION_CACHE_CONTROL
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
def normalize_headers(h)
|
80
|
+
Hash[h.map{|k,v| [k, v.is_a?(Hash) ? v : {"Cache-Control" => v} ] }]
|
81
|
+
end
|
82
|
+
|
83
|
+
end # class AutoCaching
|
84
|
+
end # module Web
|
85
|
+
end # module Startback
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Startback
|
2
|
+
module Web
|
3
|
+
#
|
4
|
+
# Sets Cross-Origin Response Headers on requests specifying an Origin
|
5
|
+
# HTTP header, according configuration passed at construction and/or
|
6
|
+
# environment variables.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# # Default configuration, using environment variables when set
|
11
|
+
# use CorsHeaders
|
12
|
+
#
|
13
|
+
# # Force a bouncing of the origin, using the Origin request header
|
14
|
+
# # as Access-Control-Allow-Origin response header
|
15
|
+
# use CorsHeaders, bounce: true
|
16
|
+
#
|
17
|
+
# # Overrides a specific header
|
18
|
+
# use CorsHeaders, headers: { 'Access-Control-Allow-Methods' => 'POST' }
|
19
|
+
#
|
20
|
+
class CorsHeaders
|
21
|
+
|
22
|
+
ALLOW_ORIGIN = ENV['STARTBACK_CORS_ALLOW_ORIGIN'] || '*'
|
23
|
+
|
24
|
+
ALLOW_METHODS = ENV['STARTBACK_CORS_ALLOW_METHODS'] || 'OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE'
|
25
|
+
|
26
|
+
ALLOW_CREDENTIALS = ENV['STARTBACK_CORS_ALLOW_CREDENTIALS'] || 'true'
|
27
|
+
|
28
|
+
MAX_AGE = ENV['STARTBACK_CORS_MAX_AGE'] || '1728000'
|
29
|
+
|
30
|
+
ALLOW_HEADERS = ENV['STARTBACK_CORS_ALLOW_HEADERS'] || 'Authorization, Content-Type, Origin, Accept, If-Modified-Since, If-Match, If-None-Match'
|
31
|
+
|
32
|
+
EXPOSE_HEADERS = ENV['STARTBACK_CORS_EXPOSE_HEADERS'] || 'Location, ETag, Last-Modified, Content-Type'
|
33
|
+
|
34
|
+
DEFAULT_CORS_HEADERS = {
|
35
|
+
'Access-Control-Allow-Origin' => ALLOW_ORIGIN,
|
36
|
+
'Access-Control-Allow-Methods' => ALLOW_METHODS,
|
37
|
+
'Access-Control-Allow-Credentials' => ALLOW_CREDENTIALS,
|
38
|
+
'Access-Control-Max-Age' => MAX_AGE,
|
39
|
+
'Access-Control-Allow-Headers' => ALLOW_HEADERS,
|
40
|
+
'Access-Control-Expose-Headers' => EXPOSE_HEADERS
|
41
|
+
}
|
42
|
+
|
43
|
+
DEFAULT_OPTIONS = {
|
44
|
+
:headers => DEFAULT_CORS_HEADERS
|
45
|
+
}
|
46
|
+
|
47
|
+
def initialize(app, options = {})
|
48
|
+
@app = app
|
49
|
+
@options = Startback::Support.deep_merge(DEFAULT_OPTIONS, options)
|
50
|
+
end
|
51
|
+
|
52
|
+
def call(env)
|
53
|
+
status, headers, body = @app.call(env)
|
54
|
+
if origin = env['HTTP_ORIGIN']
|
55
|
+
headers = cors_headers(origin).merge(headers)
|
56
|
+
end
|
57
|
+
if env['REQUEST_METHOD'] == 'OPTIONS'
|
58
|
+
headers['Content-Length'] = '0'
|
59
|
+
status, headers, body = [204, headers, []]
|
60
|
+
end
|
61
|
+
[status, headers, body]
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def cors_headers(origin)
|
67
|
+
headers = @options[:headers].dup
|
68
|
+
if bounce?
|
69
|
+
headers['Access-Control-Allow-Origin'] = origin
|
70
|
+
end
|
71
|
+
headers
|
72
|
+
end
|
73
|
+
|
74
|
+
def bounce?
|
75
|
+
@options[:bounce]
|
76
|
+
end
|
77
|
+
|
78
|
+
end # class AllowCors
|
79
|
+
end # class CorsHeaders
|
80
|
+
end # module Samback
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'sprockets'
|
2
|
+
module Startback
|
3
|
+
module Web
|
4
|
+
#
|
5
|
+
# Rack application & middleware that can be used to simplify javascript
|
6
|
+
# and css assets management, using Sprockets.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# # Used as rack app, typically under a path
|
11
|
+
# Rack::Builder.new do
|
12
|
+
# map '/assets' do
|
13
|
+
# run Startback::Web::MagicAssets.new({
|
14
|
+
# folder: "/path/to/assets/src"
|
15
|
+
# })
|
16
|
+
# end
|
17
|
+
# run MyApp
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # Used as a rack middleware, e.g. in a Sinatra application
|
21
|
+
# use Startback::Web::MagicAssets, {
|
22
|
+
# folder: "/path/to/assets/src",
|
23
|
+
# path: "/assets"
|
24
|
+
# }
|
25
|
+
#
|
26
|
+
# Sprocket configuration can be done through the `:sprocket` option:
|
27
|
+
#
|
28
|
+
# use Startback::Web::MagicAssets, {
|
29
|
+
# sprockets: {
|
30
|
+
# :css_compressor => :scss
|
31
|
+
# }
|
32
|
+
# }
|
33
|
+
#
|
34
|
+
class MagicAssets
|
35
|
+
|
36
|
+
DEFAULT_OPTIONS = {
|
37
|
+
sprockets: {}
|
38
|
+
}
|
39
|
+
|
40
|
+
def initialize(app, options = {})
|
41
|
+
app, options = nil, app if app.is_a?(Hash)
|
42
|
+
@app = app
|
43
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
44
|
+
@sprockets = build_sprockets
|
45
|
+
end
|
46
|
+
attr_reader :sprockets
|
47
|
+
|
48
|
+
def call(env)
|
49
|
+
if new_env = is_match?(env)
|
50
|
+
@sprockets.call(new_env)
|
51
|
+
else
|
52
|
+
@app.call(env)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def [](*args, &bl)
|
57
|
+
@sprockets.[](*args, &bl)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def path
|
63
|
+
@options[:path]
|
64
|
+
end
|
65
|
+
|
66
|
+
def is_match?(env)
|
67
|
+
if @app.nil?
|
68
|
+
# Not used as a middleware, use this env and match
|
69
|
+
env
|
70
|
+
elsif env['PATH_INFO'].start_with?(path)
|
71
|
+
# Used as a middleware, and PATH_INFO starts with the
|
72
|
+
# assets path => strip it for sprockets
|
73
|
+
env.merge("PATH_INFO" => env["PATH_INFO"].sub(path, ""))
|
74
|
+
else
|
75
|
+
# No match, let @app execute with the untouched environment
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def build_sprockets
|
81
|
+
Sprockets::Environment.new.tap{|s|
|
82
|
+
Array(@options[:folder]).each do |folder|
|
83
|
+
s.append_path(folder)
|
84
|
+
end
|
85
|
+
@options[:sprockets].each_pair do |k,v|
|
86
|
+
s.public_send(:"#{k}=", v)
|
87
|
+
end
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
end # class MagicAssets
|
92
|
+
end # module Web
|
93
|
+
end # module Startback
|
94
|
+
require_relative 'magic_assets/rake_tasks'
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Startback
|
2
|
+
module Web
|
3
|
+
class MagicAssets
|
4
|
+
class RakeTasks
|
5
|
+
|
6
|
+
DEFAULT_OPTIONS = {
|
7
|
+
:namespace => :assets
|
8
|
+
}
|
9
|
+
|
10
|
+
def initialize(rake, options)
|
11
|
+
@rake = rake
|
12
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
13
|
+
install
|
14
|
+
end
|
15
|
+
attr_reader :rake, :options
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def install
|
20
|
+
require 'securerandom'
|
21
|
+
|
22
|
+
ns = options[:namespace]
|
23
|
+
target_folder = options[:target]
|
24
|
+
assets = options[:assets]
|
25
|
+
assets = MagicAssets.new(assets) if assets.is_a?(Hash)
|
26
|
+
version = SecureRandom.urlsafe_base64
|
27
|
+
|
28
|
+
rake.instance_exec do
|
29
|
+
namespace(ns) do
|
30
|
+
|
31
|
+
desc 'Cleans generated assets'
|
32
|
+
task :clean do
|
33
|
+
FileUtils.rm_rf target_folder
|
34
|
+
end
|
35
|
+
|
36
|
+
task :prepare do
|
37
|
+
FileUtils.mkdir_p target_folder
|
38
|
+
(target_folder/"VERSION").write(version)
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'compile javascript assets'
|
42
|
+
task :compile_js do
|
43
|
+
assets['vendor.js'].write_to(target_folder/"vendor-#{version}.min.js")
|
44
|
+
assets['app.js'].write_to(target_folder/"app-#{version}.min.js")
|
45
|
+
puts "successfully compiled js assets"
|
46
|
+
end
|
47
|
+
|
48
|
+
desc 'compile css assets'
|
49
|
+
task :compile_css do
|
50
|
+
assets['vendor.css'].write_to(target_folder/"vendor-#{version}.min.css")
|
51
|
+
assets['app.css'].write_to(target_folder/"app-#{version}.min.css")
|
52
|
+
puts "successfully compiled css assets"
|
53
|
+
end
|
54
|
+
|
55
|
+
desc 'compile assets'
|
56
|
+
task :compile => [:clean, :prepare, :compile_js, :compile_css]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end # class MagicAssets
|
63
|
+
end # module Web
|
64
|
+
end # module Startback
|
data/spec/unit/test_support.rb
CHANGED
@@ -4,5 +4,37 @@ module Startback
|
|
4
4
|
describe Support do
|
5
5
|
include Support
|
6
6
|
|
7
|
+
describe "deep_merge" do
|
8
|
+
|
9
|
+
it 'works as expected' do
|
10
|
+
h1 = {
|
11
|
+
:foo => "bar",
|
12
|
+
:bar => "unchanged",
|
13
|
+
:baz => {
|
14
|
+
"hello" => "world",
|
15
|
+
"changed" => "yes"
|
16
|
+
}
|
17
|
+
}
|
18
|
+
h2 = {
|
19
|
+
:foo => "baz",
|
20
|
+
:baz => {
|
21
|
+
"eloy" => "tom",
|
22
|
+
"changed" => "no"
|
23
|
+
}
|
24
|
+
}
|
25
|
+
expected = {
|
26
|
+
:foo => "baz",
|
27
|
+
:bar => "unchanged",
|
28
|
+
:baz => {
|
29
|
+
"hello" => "world",
|
30
|
+
"eloy" => "tom",
|
31
|
+
"changed" => "no"
|
32
|
+
}
|
33
|
+
}
|
34
|
+
expect(deep_merge(h1, h2)).to eql(expected)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
7
39
|
end
|
8
40
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require ./app/hello
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'startback/web/auto_caching'
|
3
|
+
|
4
|
+
module Startback
|
5
|
+
module Web
|
6
|
+
describe AutoCaching do
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
context 'when used without options' do
|
10
|
+
def app
|
11
|
+
Rack::Builder.new do
|
12
|
+
use AutoCaching
|
13
|
+
run ->(env){ [200, {}, ["Hello error"]] }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'sets the development Cache-Control since this is a test' do
|
18
|
+
get '/'
|
19
|
+
expect(last_response['Cache-Control']). to eql("no-cache, no-store, max-age=0, must-revalidate")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when forcing production' do
|
24
|
+
def app
|
25
|
+
Rack::Builder.new do
|
26
|
+
use AutoCaching, false
|
27
|
+
run ->(env){ [200, {}, ["Hello error"]] }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sets the production Cache-Control' do
|
32
|
+
get '/'
|
33
|
+
expect(last_response['Cache-Control']). to eql("public, must-revalidate, max-age=3600, s-max-age=3600")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when forcing development headers' do
|
38
|
+
def app
|
39
|
+
Rack::Builder.new do
|
40
|
+
use AutoCaching, development: { "Cache-Control" => "no-cache" }
|
41
|
+
run ->(env){ [200, {}, ["Hello error"]] }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'sets the production Cache-Control' do
|
46
|
+
get '/'
|
47
|
+
expect(last_response['Cache-Control']). to eql("no-cache")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when setting the Cache-Control header only' do
|
52
|
+
def app
|
53
|
+
Rack::Builder.new do
|
54
|
+
use AutoCaching, development: "no-cache"
|
55
|
+
run ->(env){ [200, {}, ["Hello error"]] }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'sets the production Cache-Control' do
|
60
|
+
get '/'
|
61
|
+
expect(last_response['Cache-Control']). to eql("no-cache")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end # CatchAll
|
66
|
+
end # module Web
|
67
|
+
end # module Startback
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'startback/web/cors_headers'
|
3
|
+
|
4
|
+
module Startback
|
5
|
+
module Web
|
6
|
+
describe CorsHeaders do
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
context 'when used without options' do
|
10
|
+
def app
|
11
|
+
Rack::Builder.new do
|
12
|
+
use CorsHeaders
|
13
|
+
run ->(env){ [200, {}, ["Hello world"]] }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'sets the CORS headers to default values' do
|
18
|
+
header('Origin', "https://test.com")
|
19
|
+
get '/'
|
20
|
+
expect(last_response['Access-Control-Allow-Origin']). to eql("*")
|
21
|
+
expect(last_response['Access-Control-Allow-Methods']). to eql("OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE")
|
22
|
+
expect(last_response.body).to eql("Hello world")
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'strips everything when option' do
|
26
|
+
header('Origin', "https://test.com")
|
27
|
+
options '/'
|
28
|
+
expect(last_response['Access-Control-Allow-Origin']). to eql("*")
|
29
|
+
expect(last_response['Access-Control-Allow-Methods']). to eql("OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE")
|
30
|
+
expect(last_response.status).to eql(204)
|
31
|
+
expect(last_response.body).to eql("")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when used with the :bounce option' do
|
36
|
+
def app
|
37
|
+
Rack::Builder.new do
|
38
|
+
use CorsHeaders, bounce: true
|
39
|
+
run ->(env){ [200, {}, ["Hello world"]] }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'sets the CORS Origin header to the caller' do
|
44
|
+
header('Origin', "https://test.com")
|
45
|
+
get '/'
|
46
|
+
expect(last_response['Access-Control-Allow-Origin']). to eql("https://test.com")
|
47
|
+
expect(last_response['Access-Control-Allow-Methods']). to eql("OPTIONS, HEAD, GET, POST, PUT, PATCH, DELETE")
|
48
|
+
expect(last_response.body).to eql("Hello world")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'when overriding a header' do
|
53
|
+
def app
|
54
|
+
Rack::Builder.new do
|
55
|
+
use CorsHeaders, headers: { 'Access-Control-Allow-Methods' => "POST" }
|
56
|
+
run ->(env){ [200, {}, ["Hello world"]] }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'sets the CORS Origin header to the caller' do
|
61
|
+
header('Origin', "https://test.com")
|
62
|
+
get '/'
|
63
|
+
expect(last_response['Access-Control-Allow-Origin']). to eql("*")
|
64
|
+
expect(last_response['Access-Control-Allow-Methods']). to eql("POST")
|
65
|
+
expect(last_response.body).to eql("Hello world")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when the app sets specific headers' do
|
70
|
+
def app
|
71
|
+
Rack::Builder.new do
|
72
|
+
use CorsHeaders
|
73
|
+
run ->(env){ [200, {'Access-Control-Allow-Methods' => "POST"}, ["Hello world"]] }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'does not override them' do
|
78
|
+
header('Origin', "https://test.com")
|
79
|
+
get '/'
|
80
|
+
expect(last_response['Access-Control-Allow-Origin']). to eql("*")
|
81
|
+
expect(last_response['Access-Control-Allow-Methods']). to eql("POST")
|
82
|
+
expect(last_response.body).to eql("Hello world")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end # CatchAll
|
87
|
+
end # module Web
|
88
|
+
end # module Startback
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'startback/web/magic_assets'
|
3
|
+
|
4
|
+
module Startback
|
5
|
+
module Web
|
6
|
+
describe MagicAssets do
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
context 'when used as an app' do
|
10
|
+
let(:app){
|
11
|
+
MagicAssets.new({
|
12
|
+
folder: Path.dir/"fixtures/assets"
|
13
|
+
})
|
14
|
+
}
|
15
|
+
|
16
|
+
it 'works as expected' do
|
17
|
+
get "/index.js"
|
18
|
+
expect(last_response.status).to eql(200)
|
19
|
+
expect(last_response.body).to match(/function test/)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'delegates a [] call to sprockets' do
|
23
|
+
result = app['index.js']
|
24
|
+
expect(result.to_s).to match(/function test/)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'returns a 404 on unknown' do
|
28
|
+
get '/nosuchone.js'
|
29
|
+
expect(last_response.status).to eql(404)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when used as a middleware' do
|
34
|
+
let(:app){
|
35
|
+
Rack::Builder.new do
|
36
|
+
use MagicAssets, {
|
37
|
+
folder: Path.dir/"fixtures/assets",
|
38
|
+
path: "/my-assets"
|
39
|
+
}
|
40
|
+
run ->(t){
|
41
|
+
[200, {}, ["Hello world"]]
|
42
|
+
}
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
it 'lets unrelated things pass' do
|
47
|
+
get "/hello"
|
48
|
+
expect(last_response.status).to eql(200)
|
49
|
+
expect(last_response.body).to eql("Hello world")
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'serves the assets under the chosen path' do
|
53
|
+
get "/my-assets/index.js"
|
54
|
+
expect(last_response.status).to eql(200)
|
55
|
+
expect(last_response.body).to match(/function test/)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: startback
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bernard Lambeau
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -39,75 +39,103 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rack-test
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sprockets
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
48
|
-
type: :
|
61
|
+
version: 4.0.0.beta8
|
62
|
+
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
68
|
+
version: 4.0.0.beta8
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: babel-transpiler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sinatra
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
58
86
|
requirements:
|
59
87
|
- - "~>"
|
60
88
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
89
|
+
version: '2.0'
|
62
90
|
type: :runtime
|
63
91
|
prerelease: false
|
64
92
|
version_requirements: !ruby/object:Gem::Requirement
|
65
93
|
requirements:
|
66
94
|
- - "~>"
|
67
95
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
96
|
+
version: '2.0'
|
69
97
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
98
|
+
name: rack-robustness
|
71
99
|
requirement: !ruby/object:Gem::Requirement
|
72
100
|
requirements:
|
73
101
|
- - "~>"
|
74
102
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
103
|
+
version: '1.1'
|
76
104
|
type: :runtime
|
77
105
|
prerelease: false
|
78
106
|
version_requirements: !ruby/object:Gem::Requirement
|
79
107
|
requirements:
|
80
108
|
- - "~>"
|
81
109
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
110
|
+
version: '1.1'
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
112
|
+
name: finitio
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
86
114
|
requirements:
|
87
115
|
- - ">="
|
88
116
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
117
|
+
version: '0.6'
|
90
118
|
type: :runtime
|
91
119
|
prerelease: false
|
92
120
|
version_requirements: !ruby/object:Gem::Requirement
|
93
121
|
requirements:
|
94
122
|
- - ">="
|
95
123
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
124
|
+
version: '0.6'
|
97
125
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
126
|
+
name: path
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
100
128
|
requirements:
|
101
129
|
- - ">="
|
102
130
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
131
|
+
version: '1.3'
|
104
132
|
type: :runtime
|
105
133
|
prerelease: false
|
106
134
|
version_requirements: !ruby/object:Gem::Requirement
|
107
135
|
requirements:
|
108
136
|
- - ">="
|
109
137
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
138
|
+
version: '1.3'
|
111
139
|
description: Yet another ruby backend framework, I'm afraid
|
112
140
|
email: blambeau@gmail.com
|
113
141
|
executables: []
|
@@ -130,17 +158,25 @@ files:
|
|
130
158
|
- lib/startback/support.rb
|
131
159
|
- lib/startback/support/logger.rb
|
132
160
|
- lib/startback/version.rb
|
133
|
-
- lib/startback/web.rb
|
134
161
|
- lib/startback/web/api.rb
|
162
|
+
- lib/startback/web/auto_caching.rb
|
135
163
|
- lib/startback/web/catch_all.rb
|
164
|
+
- lib/startback/web/cors_headers.rb
|
136
165
|
- lib/startback/web/health_check.rb
|
166
|
+
- lib/startback/web/magic_assets.rb
|
167
|
+
- lib/startback/web/magic_assets/rake_tasks.rb
|
137
168
|
- lib/startback/web/shield.rb
|
138
169
|
- spec/spec_helper.rb
|
139
170
|
- spec/unit/context/test_middleware.rb
|
140
171
|
- spec/unit/test_operation.rb
|
141
172
|
- spec/unit/test_support.rb
|
173
|
+
- spec/unit/web/fixtures/assets/app/hello.es6
|
174
|
+
- spec/unit/web/fixtures/assets/index.es6
|
175
|
+
- spec/unit/web/test_auto_caching.rb
|
142
176
|
- spec/unit/web/test_catch_all.rb
|
177
|
+
- spec/unit/web/test_cors_headers.rb
|
143
178
|
- spec/unit/web/test_healthcheck.rb
|
179
|
+
- spec/unit/web/test_magic_assets.rb
|
144
180
|
- tasks/gem.rake
|
145
181
|
- tasks/test.rake
|
146
182
|
homepage: http://www.enspirit.be
|
data/lib/startback/web.rb
DELETED