tetrahedron 0.0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/AUTHORS +0 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +65 -0
- data/LICENSE +0 -0
- data/README.md +4 -0
- data/Rakefile +2 -0
- data/bin/tetrahedron +4 -0
- data/config/app.ru +21 -0
- data/config/application.rb +8 -0
- data/config/environment.rb +18 -0
- data/config/puma.rb +29 -0
- data/lib/tetrahedron.rb +59 -0
- data/lib/tetrahedron/app.rb +16 -0
- data/lib/tetrahedron/assets.rb +43 -0
- data/lib/tetrahedron/base.rb +34 -0
- data/lib/tetrahedron/bootfile.rb +43 -0
- data/lib/tetrahedron/bundler.rb +10 -0
- data/lib/tetrahedron/configuration.rb +44 -0
- data/lib/tetrahedron/controller.rb +17 -0
- data/lib/tetrahedron/database.rb +113 -0
- data/lib/tetrahedron/endpoint.rb +4 -0
- data/lib/tetrahedron/environment.rb +28 -0
- data/lib/tetrahedron/error.rb +4 -0
- data/lib/tetrahedron/errors.rb +7 -0
- data/lib/tetrahedron/gem.rb +58 -0
- data/lib/tetrahedron/model.rb +32 -0
- data/lib/tetrahedron/rake.rb +16 -0
- data/lib/tetrahedron/redis.rb +4 -0
- data/lib/tetrahedron/sessions.rb +28 -0
- data/lib/tetrahedron/tasks/tet.rake +5 -0
- data/tetrahedron.gemspec +34 -0
- metadata +174 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f11bb282f14b3b5c27899cdb70fef27f4154dc20
|
4
|
+
data.tar.gz: a04eafc6d60df291569009131ee564c5fc404b44
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: acb35b1fe3d7a07905a753ff2d8422db1478a097f22d18840230085cf14b8c108e0257a5c71ebc10e5969c7bd8eb3ef565638ed343f883ae8512b4195c47dce6
|
7
|
+
data.tar.gz: 1c7365409ba0e9bf0d686a849df7d4a445730a23685af903c50bb2193e702960224be195dc8cc0e0265a5d0f33fd39ed3057443cb7f7f834456ef9b97794a2ab
|
data/.gitignore
ADDED
data/AUTHORS
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
tetrahedron (0.0.0.1)
|
5
|
+
activesupport
|
6
|
+
sequel
|
7
|
+
sinatra
|
8
|
+
sinatra-contrib
|
9
|
+
thor
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: https://rubygems.org/
|
13
|
+
specs:
|
14
|
+
activesupport (4.2.5)
|
15
|
+
i18n (~> 0.7)
|
16
|
+
json (~> 1.7, >= 1.7.7)
|
17
|
+
minitest (~> 5.1)
|
18
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
19
|
+
tzinfo (~> 1.1)
|
20
|
+
backports (3.6.7)
|
21
|
+
coderay (1.1.0)
|
22
|
+
i18n (0.7.0)
|
23
|
+
json (1.8.3)
|
24
|
+
method_source (0.8.2)
|
25
|
+
minitest (5.8.3)
|
26
|
+
multi_json (1.11.2)
|
27
|
+
pry (0.10.3)
|
28
|
+
coderay (~> 1.1.0)
|
29
|
+
method_source (~> 0.8.1)
|
30
|
+
slop (~> 3.4)
|
31
|
+
rack (1.6.4)
|
32
|
+
rack-protection (1.5.3)
|
33
|
+
rack
|
34
|
+
rack-test (0.6.3)
|
35
|
+
rack (>= 1.0)
|
36
|
+
rake (10.4.2)
|
37
|
+
sequel (4.28.0)
|
38
|
+
sinatra (1.4.6)
|
39
|
+
rack (~> 1.4)
|
40
|
+
rack-protection (~> 1.4)
|
41
|
+
tilt (>= 1.3, < 3)
|
42
|
+
sinatra-contrib (1.4.6)
|
43
|
+
backports (>= 2.0)
|
44
|
+
multi_json
|
45
|
+
rack-protection
|
46
|
+
rack-test
|
47
|
+
sinatra (~> 1.4.0)
|
48
|
+
tilt (>= 1.3, < 3)
|
49
|
+
slop (3.6.0)
|
50
|
+
thor (0.19.1)
|
51
|
+
thread_safe (0.3.5)
|
52
|
+
tilt (2.0.1)
|
53
|
+
tzinfo (1.2.2)
|
54
|
+
thread_safe (~> 0.1)
|
55
|
+
|
56
|
+
PLATFORMS
|
57
|
+
ruby
|
58
|
+
|
59
|
+
DEPENDENCIES
|
60
|
+
pry
|
61
|
+
rake
|
62
|
+
tetrahedron!
|
63
|
+
|
64
|
+
BUNDLED WITH
|
65
|
+
1.10.6
|
data/LICENSE
ADDED
File without changes
|
data/README.md
ADDED
data/Rakefile
ADDED
data/bin/tetrahedron
ADDED
data/config/app.ru
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env rackup
|
2
|
+
|
3
|
+
require_relative 'application'
|
4
|
+
require_relative 'environment'
|
5
|
+
|
6
|
+
# TODO(mtwilliams): Request tracking (make Heroku compatiable).
|
7
|
+
# TODO(mtwilliams): Performance monitoring (make Heroku compatiable).
|
8
|
+
# TODO(mtwilliams): Error handling.
|
9
|
+
|
10
|
+
if Tetrahedron::Sessions.configured?
|
11
|
+
use Rack::Session::Cookie, :key => Tetrahedron::Sessions.config.cookie,
|
12
|
+
:domain => Tetrahedron::Sessions.config.domain,
|
13
|
+
:expire_after => Tetrahedron::Sessions.config.lifetime,
|
14
|
+
:secret => Tetrahedron::Sessions.config.secret
|
15
|
+
else
|
16
|
+
# TODO(mtwilliams): Use a logger.
|
17
|
+
$stderr.puts "Sessions not configured!\n => Use Tetrahedron::Sessions.configure in config/environment.rb!"
|
18
|
+
end
|
19
|
+
|
20
|
+
# TODO(mtwilliams): Remove cylcical dependency so we can use a constantized version.
|
21
|
+
run Tetrahedron.config.app.constantize
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'application'
|
2
|
+
|
3
|
+
Tetrahedron::Environment.load
|
4
|
+
|
5
|
+
if Tetrahedron.env.development?
|
6
|
+
# Synchronize STDOUT and STDERR to make debugging easier.
|
7
|
+
STDOUT.sync = STDERR.sync = true
|
8
|
+
end
|
9
|
+
|
10
|
+
require File.join(Tetrahedron.config.root, "config", "environment")
|
11
|
+
|
12
|
+
unless Tetrahedron::Database.configured?
|
13
|
+
$stderr.puts "Database not configured!\n => Use Tetrahedron::Database.configure in config/environment.rb!"
|
14
|
+
end
|
15
|
+
|
16
|
+
# unless Tetrahedron::Redis.configured?
|
17
|
+
# $stderr.puts "Redis not configured!\n => Use Tetrahedron::Redis.configure in config/environment.rb!"
|
18
|
+
# end
|
data/config/puma.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative 'application'
|
2
|
+
require_relative 'environment'
|
3
|
+
|
4
|
+
rackup File.join(Tetrahedron.root, 'config', 'app.ru')
|
5
|
+
|
6
|
+
environment Tetrahedron.env.to_s
|
7
|
+
bind "tcp://#{ENV['HOST'] || '0.0.0.0'}:#{ENV['PORT'] || 80}"
|
8
|
+
port (ENV['PORT'] || 80).to_i
|
9
|
+
workers (ENV['PUMA_WORKERS'] || 1).to_i
|
10
|
+
threads (ENV['PUMA_MIN_THREADS'] || 1).to_i,
|
11
|
+
(ENV['PUMA_MAX_THREADS'] || 1).to_i
|
12
|
+
|
13
|
+
preload_app!
|
14
|
+
|
15
|
+
before_fork do
|
16
|
+
if Tetrahedron.env.production?
|
17
|
+
# TODO(mtwilliams): Precompile assets.
|
18
|
+
Tetrahedron::Assets.precompile
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
on_worker_boot do
|
23
|
+
Tetrahedron.bootfile.up.call() if Tetrahedron.bootfile.up
|
24
|
+
end
|
25
|
+
|
26
|
+
# TODO(mtwilliams): Report low-level errors in production.
|
27
|
+
# lowlevel_error_handler do |e|
|
28
|
+
# ...
|
29
|
+
# end
|
data/lib/tetrahedron.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
Bundler.require(:default)
|
5
|
+
|
6
|
+
# Some things aren't nicely cut gems, unforunately.
|
7
|
+
require 'base64'
|
8
|
+
require 'erb'
|
9
|
+
require 'net/http'
|
10
|
+
require 'ostruct'
|
11
|
+
require 'securerandom'
|
12
|
+
require 'socket'
|
13
|
+
require 'time'
|
14
|
+
require 'uri'
|
15
|
+
require 'yaml'
|
16
|
+
|
17
|
+
# Smells like Rails...
|
18
|
+
require 'active_support'
|
19
|
+
require 'active_support/core_ext'
|
20
|
+
|
21
|
+
module Tetrahedron
|
22
|
+
def self.root
|
23
|
+
@root ||= File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'tetrahedron/gem'
|
28
|
+
require 'tetrahedron/bundler'
|
29
|
+
|
30
|
+
require 'tetrahedron/error'
|
31
|
+
require 'tetrahedron/errors'
|
32
|
+
|
33
|
+
require 'tetrahedron/configuration'
|
34
|
+
require 'tetrahedron/environment'
|
35
|
+
require 'tetrahedron/bootfile'
|
36
|
+
|
37
|
+
Tetrahedron::Bundler.require
|
38
|
+
|
39
|
+
require 'sinatra/base'
|
40
|
+
require 'sinatra/contrib'
|
41
|
+
|
42
|
+
require 'sequel'
|
43
|
+
|
44
|
+
Sequel.extension :migration
|
45
|
+
|
46
|
+
Sequel::Model.plugin :timestamps
|
47
|
+
Sequel::Model.plugin :subclasses
|
48
|
+
|
49
|
+
require 'tetrahedron/base'
|
50
|
+
|
51
|
+
require 'tetrahedron/assets'
|
52
|
+
|
53
|
+
require 'tetrahedron/database'
|
54
|
+
require 'tetrahedron/sessions'
|
55
|
+
|
56
|
+
require 'tetrahedron/app'
|
57
|
+
require 'tetrahedron/model'
|
58
|
+
require 'tetrahedron/controller'
|
59
|
+
require 'tetrahedron/endpoint'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Tetrahedron
|
2
|
+
class App < Tetrahedron::Base
|
3
|
+
configure do
|
4
|
+
environment = Tetrahedron::Assets::Environment.new
|
5
|
+
set :assets, environment: environment
|
6
|
+
sprockets_based_server = Tetrahedron::Assets::Server.new(settings.assets[:environment])
|
7
|
+
set :assets, server: sprockets_based_server
|
8
|
+
end
|
9
|
+
|
10
|
+
get %r{^/assets/*} do
|
11
|
+
# Assets are handled by Sprockets.
|
12
|
+
env['PATH_INFO'].sub('/assets', '')
|
13
|
+
settings.assets[:server].call!(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Tetrahedron
|
2
|
+
module Assets
|
3
|
+
class Environment < Sprockets::Environment
|
4
|
+
def initialize
|
5
|
+
super(Tetrahedron.config.root)
|
6
|
+
|
7
|
+
self.append_path('vendor/assets/styles')
|
8
|
+
self.append_path('vendor/assets/scripts')
|
9
|
+
self.append_path('vendor/assets/fonts')
|
10
|
+
self.append_path('vendor/assets/images')
|
11
|
+
|
12
|
+
self.append_path('assets/styles')
|
13
|
+
self.append_path('assets/scripts')
|
14
|
+
self.append_path('assets/fonts')
|
15
|
+
self.append_path('assets/images')
|
16
|
+
|
17
|
+
if Tetrahedron.env.production?
|
18
|
+
self.css_compressor = CSSminify.new
|
19
|
+
self.js_compressor = Uglifier.new
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Server
|
25
|
+
def initialize(environment)
|
26
|
+
@environment = environment
|
27
|
+
end
|
28
|
+
|
29
|
+
def call(env)
|
30
|
+
@environment.call(env)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Precompiler
|
35
|
+
# Precompile.
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.precompile
|
39
|
+
# TODO(mtwilliams): Implement asset precompilation.
|
40
|
+
$stderr.puts "Asset precompilation is not implemented yet!"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Tetrahedron
|
2
|
+
class Base < Sinatra::Base
|
3
|
+
set :environment, Tetrahedron.env.to_sym
|
4
|
+
set :root, Tetrahedron.config.root
|
5
|
+
|
6
|
+
# Don't use the built-in web-server.
|
7
|
+
set :run, false
|
8
|
+
|
9
|
+
# Errors are bubbled so they can be handled by middleware.
|
10
|
+
set :dump_errors, false
|
11
|
+
set :raise_errors, true
|
12
|
+
set :show_exceptions, false
|
13
|
+
|
14
|
+
# Fuck you, IE9.
|
15
|
+
disable :method_override
|
16
|
+
|
17
|
+
configure :development do
|
18
|
+
# King of iteration, baby.
|
19
|
+
register Sinatra::Reloader
|
20
|
+
end
|
21
|
+
|
22
|
+
configure do
|
23
|
+
disable :static
|
24
|
+
end
|
25
|
+
|
26
|
+
get '/*' do
|
27
|
+
# Try to serve statically first.
|
28
|
+
server = Rack::Static.new(proc {[404, {}, []]}, root: File.join(Tetrahedron.config.root, '/public'))
|
29
|
+
status, headers, response = server.call(env)
|
30
|
+
pass if status == 404
|
31
|
+
[status, headers, response]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Tetrahedron
|
2
|
+
class Bootfile
|
3
|
+
DEFAULT = "config/boot.rb"
|
4
|
+
|
5
|
+
attr_reader :up
|
6
|
+
attr_reader :down
|
7
|
+
|
8
|
+
def initialize()
|
9
|
+
# TODO(mtwilliams): Provide reasonable defaults.
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.load(filename=Tetrahedron::Bootfile::DEFAULT)
|
13
|
+
bootfile = Tetrahedron::Bootfile.new
|
14
|
+
require File.join(Tetrahedron.root, "config", "application")
|
15
|
+
DomainSpecificLanguage.for(bootfile).instance_eval(File.read(filename), filename)
|
16
|
+
bootfile
|
17
|
+
end
|
18
|
+
|
19
|
+
class DomainSpecificLanguage
|
20
|
+
def initialize(bootfile)
|
21
|
+
@bootfile = bootfile
|
22
|
+
end
|
23
|
+
|
24
|
+
def up(&block)
|
25
|
+
@bootfile.instance_variable_set(:@up, block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def down(&block)
|
29
|
+
@bootfile.instance_variable_set(:@down, block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.for(bootfile)
|
33
|
+
DomainSpecificLanguage.new(bootfile)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.bootfile(path=Tetrahedron::Bootfile::DEFAULT)
|
39
|
+
@bootfile ||= begin
|
40
|
+
Bootfile.load(path)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Tetrahedron
|
2
|
+
class Configuration
|
3
|
+
DEFAULT = "config/tetrahedron.rb"
|
4
|
+
|
5
|
+
attr_reader :app
|
6
|
+
attr_reader :root
|
7
|
+
|
8
|
+
def initialize()
|
9
|
+
# TODO(mtwilliams): Best guess @app and @root.
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.load(filename=Tetrahedron::Configuration::DEFAULT)
|
13
|
+
configuration = Tetrahedron::Configuration.new
|
14
|
+
DomainSpecificLanguage.for(configuration).instance_eval(File.read(filename), filename)
|
15
|
+
configuration
|
16
|
+
end
|
17
|
+
|
18
|
+
class DomainSpecificLanguage
|
19
|
+
def initialize(configuration)
|
20
|
+
@configuration = configuration
|
21
|
+
end
|
22
|
+
|
23
|
+
def app(name)
|
24
|
+
@configuration.instance_variable_set(:@app, name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def root(directory)
|
28
|
+
raise Tetrahedron::MisconfiguredError.new(:root, 'the path given does not exist') unless File.exist?(directory)
|
29
|
+
raise Tetrahedron::MisconfiguredError.new(:root, 'the path given is not a directory') unless File.directory?(directory)
|
30
|
+
@configuration.instance_variable_set(:@root, directory)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.for(configuration)
|
34
|
+
DomainSpecificLanguage.new(configuration)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.config(path=Tetrahedron::Configuration::DEFAULT)
|
40
|
+
@configuration ||= begin
|
41
|
+
Configuration.load(path)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Tetrahedron
|
2
|
+
class Controller < Tetrahedron::Base
|
3
|
+
set :views, File.join(Tetrahedron.config.root, 'app', Tetrahedron.config.app.underscore, 'views')
|
4
|
+
|
5
|
+
# Use app/<app>/views/_layouts/default as the default layout.
|
6
|
+
set :erb, :layout => :'_layouts/default'
|
7
|
+
|
8
|
+
# Recognize *.html.erb as Erubis templates.
|
9
|
+
Tilt.register Tilt::ErubisTemplate, 'html.erb'
|
10
|
+
|
11
|
+
# Don't recognize *.erb templates.
|
12
|
+
def erb(template, options={}, locals={})
|
13
|
+
options[:layout] = settings.erb[:layout] if options[:layout].nil?
|
14
|
+
render 'html.erb', template, options, locals
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Tetrahedron
|
2
|
+
module Database
|
3
|
+
class Configuration
|
4
|
+
Options = %i{adapter user password host port database pool}
|
5
|
+
attr_reader *Options
|
6
|
+
|
7
|
+
def initialize(configuration={})
|
8
|
+
Options.each do |opt|
|
9
|
+
self.instance_variable_set(:"@#{opt}", configuration[opt])
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def override(overrides={})
|
14
|
+
configuration = self.dup
|
15
|
+
Options.each do |opt|
|
16
|
+
configuration.instance_variable_set(:"@#{opt}", overrides[opt]) if overrides.include? opt
|
17
|
+
end
|
18
|
+
configuration
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.configured?
|
23
|
+
!@configuration.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.configure(configuration)
|
27
|
+
# TODO(mtwilliams): Validate |configuration|.
|
28
|
+
configuration[:database] ||= Terahedron.config.app.to_s.underscore.gsub(/::/,'_')
|
29
|
+
configuration[:pool] ||= 1
|
30
|
+
@configuration = Configuration.new(configuration)
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO(mtwilliams): Refactor into Terahedron::Service.wait_until_reachable
|
34
|
+
def self.wait_until_reachable(overrides={})
|
35
|
+
# By default, we wait up to 15 seconds.
|
36
|
+
overrides[:timeout] ||= 15
|
37
|
+
|
38
|
+
configuration = @configuration.override(overrides) if configured?
|
39
|
+
configuration ||= Configuration.new(overrides)
|
40
|
+
|
41
|
+
# TODO(mtwilliams): Don't assume TCP.
|
42
|
+
# Look at |configuration.adapter|.
|
43
|
+
Timeout::timeout(overrides[:timeout]) do
|
44
|
+
while true
|
45
|
+
begin
|
46
|
+
TCPSocket.new(configuration.host, configuration.port).close
|
47
|
+
return true
|
48
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError
|
49
|
+
# We're all good citizens, right?
|
50
|
+
Kernel.sleep(1)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
rescue Timeout::Error
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.wait_until_reachable!(overrides={})
|
59
|
+
# TODO(mtwilliams): Raise a custom exception type.
|
60
|
+
raise "Database is unreachable!" unless wait_until_reachable
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.connect(overrides={})
|
64
|
+
# Wait some amount of time before assuming the database is down.
|
65
|
+
Tetrahedron::Database.wait_until_reachable!(overrides)
|
66
|
+
|
67
|
+
configuration = @configuration.override(overrides) if configured?
|
68
|
+
configuration ||= Configuration.new(overrides)
|
69
|
+
|
70
|
+
@connection = Sequel.connect(:adapter => configuration.adapter,
|
71
|
+
:user => configuration.user,
|
72
|
+
:password => configuration.password,
|
73
|
+
:host => configuration.host,
|
74
|
+
:port => configuration.port,
|
75
|
+
:database => configuration.database,
|
76
|
+
:test => true,
|
77
|
+
:sslmode => :prefer,
|
78
|
+
:max_connections => configuration.pool)
|
79
|
+
|
80
|
+
# Reback our models with the new connection pool.
|
81
|
+
Tetrahedron::Model.db = @connection
|
82
|
+
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.disconnect
|
87
|
+
connection = @connection
|
88
|
+
|
89
|
+
# TODO(mtwilliams): Ensure this is thread safe.
|
90
|
+
# Make sure no one tries to use the disconnected connection pool.
|
91
|
+
Tetrahedron::Model.db = Sequel.mock
|
92
|
+
@connection = nil
|
93
|
+
|
94
|
+
connection.disconnect unless connection.nil?
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.connection
|
98
|
+
@connection
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.reset
|
102
|
+
# TODO(mtwilliams): Drop and recreate database.
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.migrate
|
106
|
+
Sequel::IntegerMigrator.run(connection, File.join(Tetrahedron.config.root, 'db', 'migrations'))
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.seed
|
110
|
+
Kernel.load(File.join(Tetrahedron.config.root, 'db', 'seed.rb'))
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Tetrahedron
|
2
|
+
module Environment
|
3
|
+
# Loads `ENV` from `.env` if available.
|
4
|
+
def self.load
|
5
|
+
# TODO(mtwilliams): Use a proper logger.
|
6
|
+
$stdout.puts "Loading environment from `.env` if available..."
|
7
|
+
require 'dotenv'
|
8
|
+
Dotenv.load! File.join(Tetrahedron.config.root, '.env')
|
9
|
+
$stdout.puts " => Loaded from `.env`."
|
10
|
+
rescue LoadError => _
|
11
|
+
$stderr.puts " => The 'dotenv' Gem is not available on this system."
|
12
|
+
false
|
13
|
+
rescue Errno::ENOENT
|
14
|
+
$stderr.puts " => No `.env' file."
|
15
|
+
false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.env
|
20
|
+
env = ENV["#{Tetrahedron.config.app.upcase.gsub(/::/,'_')}_ENV"] || ENV['TETRAHEDRON_ENV'] || ENV['RACK_ENV']
|
21
|
+
@env ||= ::ActiveSupport::StringInquirer.new(env)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.env=(new_environment)
|
25
|
+
ENV["#{Tetrahedron.config.app.upcase.gsub(/::/,'_')}_ENV"] = ENV['TETRAHEDRON_ENV'] = new_environment
|
26
|
+
@env = ::ActiveSupport::StringInquirer.new(new_environment)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Tetrahedron
|
4
|
+
module Gem
|
5
|
+
# The name of this Gem.
|
6
|
+
def self.name
|
7
|
+
"tetrahedron"
|
8
|
+
end
|
9
|
+
|
10
|
+
# The name and email address of the primary author.
|
11
|
+
def self.author
|
12
|
+
self.authors.first
|
13
|
+
end
|
14
|
+
|
15
|
+
# The name and email addresses of all authors.
|
16
|
+
def self.authors
|
17
|
+
[["Michael Williams", "m.t.williams@live.com"]].map do |author|
|
18
|
+
name, email = author
|
19
|
+
OpenStruct.new(name: name, email: email)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# This Gem's homepage URL.
|
24
|
+
def self.homepage
|
25
|
+
"http://github.com/mtwilliams/tetrahedron"
|
26
|
+
end
|
27
|
+
|
28
|
+
# This Gem's URL.
|
29
|
+
def self.url
|
30
|
+
"https://rubygems.org/gems/#{self.name}"
|
31
|
+
end
|
32
|
+
|
33
|
+
# A short summary of this Gem.
|
34
|
+
def self.summary
|
35
|
+
"Opinionated Sinatra."
|
36
|
+
end
|
37
|
+
|
38
|
+
# A full description of this Gem.
|
39
|
+
def self.description
|
40
|
+
"Tetrahedron is an opinionated web development framework."
|
41
|
+
end
|
42
|
+
|
43
|
+
module VERSION #:nodoc:
|
44
|
+
MAJOR, MINOR, PATCH, PRE = [0, 0, 0, 1]
|
45
|
+
STRING = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
|
46
|
+
end
|
47
|
+
|
48
|
+
# The semantic version of the this Gem.
|
49
|
+
def self.version
|
50
|
+
Gem::VERSION::STRING
|
51
|
+
end
|
52
|
+
|
53
|
+
# The license covering Tetrahedron.
|
54
|
+
def self.license
|
55
|
+
"Public Domain"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Tetrahedron
|
2
|
+
Model = Class.new(Sequel::Model)
|
3
|
+
|
4
|
+
# Stop Sequel from bitching if it's subclassed before the first database
|
5
|
+
# connection is established.
|
6
|
+
Model.db = Sequel.mock if Sequel::DATABASES.empty?
|
7
|
+
|
8
|
+
class Model
|
9
|
+
def self::db=(db)
|
10
|
+
super(db)
|
11
|
+
|
12
|
+
# All the way down, boys.
|
13
|
+
self.descendents.each do |subclass|
|
14
|
+
subclass.db = db
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self::Model(source)
|
20
|
+
unless Sequel::Model::ANONYMOUS_MODEL_CLASSES.key?(source)
|
21
|
+
anonymous_model_class = nil
|
22
|
+
if source.is_a?(Sequel::Database)
|
23
|
+
anonymous_model_class = Class.new(Tetrahedron::Model)
|
24
|
+
anonymous_model_class.db = source
|
25
|
+
else
|
26
|
+
anonymous_model_class = Class.new(Tetrahedron::Model).set_dataset(source)
|
27
|
+
end
|
28
|
+
Sequel::Model::ANONYMOUS_MODEL_CLASSES[source] = anonymous_model_class
|
29
|
+
end
|
30
|
+
return Sequel::Model::ANONYMOUS_MODEL_CLASSES[source]
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Tetrahedron
|
2
|
+
module Rake
|
3
|
+
def self.install
|
4
|
+
root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
5
|
+
require_relative 'configuration'
|
6
|
+
Tetrahedron::Configuration.load
|
7
|
+
require_relative 'environment'
|
8
|
+
Tetrahedron::Environment.load
|
9
|
+
require File.join(root, 'config', 'application')
|
10
|
+
require File.join(root, 'config', 'environment')
|
11
|
+
Dir.glob(File.join(root, 'lib', 'tetrahedron', 'tasks', '**.rake')).each do |path|
|
12
|
+
Kernel.load(path)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Tetrahedron
|
2
|
+
module Sessions
|
3
|
+
class Configuration
|
4
|
+
Options = %i{cookie domain lifetime secret}
|
5
|
+
attr_reader *Options
|
6
|
+
|
7
|
+
def initialize(configuration={})
|
8
|
+
Options.each do |opt|
|
9
|
+
self.instance_variable_set(:"@#{opt}", configuration[opt])
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.config
|
15
|
+
@configuration
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.configured?
|
19
|
+
!@configuration.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.configure(configuration)
|
23
|
+
# TODO(mtwilliams): Validate |configuration|.
|
24
|
+
configuration[:cookie] ||= "#{Tetrahedron.config.app.to_s.downcase.gsub(/::/,'.')}.session"
|
25
|
+
@configuration = Sessions::Configuration.new(configuration)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/tetrahedron.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
$:.push File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
|
2
|
+
require 'tetrahedron/gem'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = Tetrahedron::Gem.name
|
6
|
+
s.version = Tetrahedron::Gem.version
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.author = Tetrahedron::Gem.author.name
|
9
|
+
s.email = Tetrahedron::Gem.author.email
|
10
|
+
s.homepage = Tetrahedron::Gem.homepage
|
11
|
+
s.summary = Tetrahedron::Gem.summary
|
12
|
+
s.description = Tetrahedron::Gem.description
|
13
|
+
s.license = Tetrahedron::Gem.license
|
14
|
+
|
15
|
+
s.required_ruby_version = '>= 2.2.3'
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
|
21
|
+
s.require_paths = %w(lib)
|
22
|
+
|
23
|
+
s.add_development_dependency("rake")
|
24
|
+
s.add_development_dependency("pry")
|
25
|
+
|
26
|
+
s.add_dependency("activesupport")
|
27
|
+
|
28
|
+
s.add_dependency("sinatra")
|
29
|
+
s.add_dependency("sinatra-contrib")
|
30
|
+
|
31
|
+
s.add_dependency("sequel")
|
32
|
+
|
33
|
+
s.add_dependency("thor")
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tetrahedron
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Williams
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activesupport
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
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: sinatra
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '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'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sinatra-contrib
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
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: sequel
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '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'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: thor
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Tetrahedron is an opinionated web development framework.
|
112
|
+
email: m.t.williams@live.com
|
113
|
+
executables:
|
114
|
+
- tetrahedron
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- ".gitignore"
|
119
|
+
- AUTHORS
|
120
|
+
- Gemfile
|
121
|
+
- Gemfile.lock
|
122
|
+
- LICENSE
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- bin/tetrahedron
|
126
|
+
- config/app.ru
|
127
|
+
- config/application.rb
|
128
|
+
- config/environment.rb
|
129
|
+
- config/puma.rb
|
130
|
+
- lib/tetrahedron.rb
|
131
|
+
- lib/tetrahedron/app.rb
|
132
|
+
- lib/tetrahedron/assets.rb
|
133
|
+
- lib/tetrahedron/base.rb
|
134
|
+
- lib/tetrahedron/bootfile.rb
|
135
|
+
- lib/tetrahedron/bundler.rb
|
136
|
+
- lib/tetrahedron/configuration.rb
|
137
|
+
- lib/tetrahedron/controller.rb
|
138
|
+
- lib/tetrahedron/database.rb
|
139
|
+
- lib/tetrahedron/endpoint.rb
|
140
|
+
- lib/tetrahedron/environment.rb
|
141
|
+
- lib/tetrahedron/error.rb
|
142
|
+
- lib/tetrahedron/errors.rb
|
143
|
+
- lib/tetrahedron/gem.rb
|
144
|
+
- lib/tetrahedron/model.rb
|
145
|
+
- lib/tetrahedron/rake.rb
|
146
|
+
- lib/tetrahedron/redis.rb
|
147
|
+
- lib/tetrahedron/sessions.rb
|
148
|
+
- lib/tetrahedron/tasks/tet.rake
|
149
|
+
- tetrahedron.gemspec
|
150
|
+
homepage: http://github.com/mtwilliams/tetrahedron
|
151
|
+
licenses:
|
152
|
+
- Public Domain
|
153
|
+
metadata: {}
|
154
|
+
post_install_message:
|
155
|
+
rdoc_options: []
|
156
|
+
require_paths:
|
157
|
+
- lib
|
158
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: 2.2.3
|
163
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: '0'
|
168
|
+
requirements: []
|
169
|
+
rubyforge_project:
|
170
|
+
rubygems_version: 2.4.5.1
|
171
|
+
signing_key:
|
172
|
+
specification_version: 4
|
173
|
+
summary: Opinionated Sinatra.
|
174
|
+
test_files: []
|