startback 0.3.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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