lotusrb 0.0.0 → 0.1.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/{LICENSE.txt → LICENSE.md} +0 -0
- data/README.md +436 -8
- data/lib/lotus.rb +10 -0
- data/lib/lotus/application.rb +121 -0
- data/lib/lotus/config/assets.rb +29 -0
- data/lib/lotus/config/load_paths.rb +27 -0
- data/lib/lotus/config/mapper.rb +36 -0
- data/lib/lotus/config/mapping.rb +12 -0
- data/lib/lotus/config/routes.rb +16 -0
- data/lib/lotus/configuration.rb +961 -0
- data/lib/lotus/frameworks.rb +40 -0
- data/lib/lotus/loader.rb +92 -0
- data/lib/lotus/middleware.rb +42 -0
- data/lib/lotus/rendering_policy.rb +69 -0
- data/lib/lotus/routes.rb +95 -0
- data/lib/lotus/routing/default.rb +25 -0
- data/lib/lotus/templates/default.html.erb +9 -0
- data/lib/lotus/version.rb +6 -0
- data/lib/lotus/views/default.rb +20 -0
- data/lib/lotus/views/null_view.rb +17 -0
- data/lib/lotusrb.rb +1 -5
- data/lotusrb.gemspec +21 -14
- metadata +126 -21
- data/.gitignore +0 -17
- data/Gemfile +0 -4
- data/Rakefile +0 -1
- data/lib/lotusrb/version.rb +0 -3
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'lotus/router'
|
2
|
+
require 'lotus/controller'
|
3
|
+
require 'lotus/view'
|
4
|
+
|
5
|
+
# FIXME Ideally, this should be done like this:
|
6
|
+
#
|
7
|
+
# module Lotus
|
8
|
+
# module Frameworks
|
9
|
+
# module Action
|
10
|
+
# module Rack
|
11
|
+
# protected
|
12
|
+
# def response
|
13
|
+
# [super, self].flatten
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# Lotus::Action::Rack.class_eval do
|
21
|
+
# include Lotus::Frameworks::Action::Rack
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# ..but it doesn't work and I want to ship it!
|
25
|
+
|
26
|
+
Lotus::Action::Rack.class_eval do
|
27
|
+
DEFAULT_RESPONSE_CODE = 200
|
28
|
+
DEFAULT_RESPONSE_BODY = []
|
29
|
+
|
30
|
+
protected
|
31
|
+
def response
|
32
|
+
[ @_status || DEFAULT_RESPONSE_CODE, headers, @_body || DEFAULT_RESPONSE_BODY.dup, self ]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Lotus::Action.class_eval do
|
37
|
+
def to_rendering
|
38
|
+
exposures.merge(format: format)
|
39
|
+
end
|
40
|
+
end
|
data/lib/lotus/loader.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'lotus/utils/class'
|
2
|
+
require 'lotus/utils/kernel'
|
3
|
+
require 'lotus/utils/string'
|
4
|
+
require 'lotus/routes'
|
5
|
+
require 'lotus/routing/default'
|
6
|
+
|
7
|
+
module Lotus
|
8
|
+
# Load an application
|
9
|
+
#
|
10
|
+
# @since 0.1.0
|
11
|
+
# @api private
|
12
|
+
class Loader
|
13
|
+
def initialize(application)
|
14
|
+
@application = application
|
15
|
+
@configuration = @application.configuration
|
16
|
+
|
17
|
+
@mutex = Mutex.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def load!
|
21
|
+
@mutex.synchronize do
|
22
|
+
load_configuration!
|
23
|
+
load_frameworks!
|
24
|
+
load_application!
|
25
|
+
finalize!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
attr_reader :application, :configuration
|
31
|
+
|
32
|
+
def load_configuration!
|
33
|
+
configuration.load!(application_module)
|
34
|
+
end
|
35
|
+
|
36
|
+
def load_frameworks!
|
37
|
+
config = configuration
|
38
|
+
|
39
|
+
unless application_module.const_defined?('Controller')
|
40
|
+
controller = Lotus::Controller.duplicate(application_module) do
|
41
|
+
default_format config.default_format
|
42
|
+
end
|
43
|
+
|
44
|
+
application_module.const_set('Controller', controller)
|
45
|
+
end
|
46
|
+
|
47
|
+
unless application_module.const_defined?('View')
|
48
|
+
view = Lotus::View.duplicate(application_module) do
|
49
|
+
root config.templates
|
50
|
+
layout config.layout
|
51
|
+
end
|
52
|
+
|
53
|
+
application_module.const_set('View', view)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def load_application!
|
58
|
+
configuration.load_paths.load!(configuration.root)
|
59
|
+
namespace = configuration.namespace || application_module
|
60
|
+
|
61
|
+
resolver = Lotus::Routing::EndpointResolver.new(pattern: configuration.controller_pattern, namespace: namespace)
|
62
|
+
default_app = Lotus::Routing::Default.new
|
63
|
+
application.routes = Lotus::Router.new(
|
64
|
+
resolver: resolver,
|
65
|
+
default_app: default_app,
|
66
|
+
scheme: configuration.scheme,
|
67
|
+
host: configuration.host,
|
68
|
+
port: configuration.port,
|
69
|
+
&configuration.routes
|
70
|
+
)
|
71
|
+
|
72
|
+
application.middleware # preload
|
73
|
+
end
|
74
|
+
|
75
|
+
def finalize!
|
76
|
+
unless application_module.const_defined?('Routes')
|
77
|
+
routes = Lotus::Routes.new(application.routes)
|
78
|
+
application_module.const_set('Routes', routes)
|
79
|
+
end
|
80
|
+
|
81
|
+
application_module.module_eval %{
|
82
|
+
#{ application_module }::View.load!
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
def application_module
|
87
|
+
@application_module ||= Utils::Class.load!(
|
88
|
+
Utils::String.new(application.class).namespace
|
89
|
+
)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Lotus
|
2
|
+
# Rack middleware stack for an application
|
3
|
+
#
|
4
|
+
# @since 0.1.0
|
5
|
+
# @api private
|
6
|
+
class Middleware
|
7
|
+
# Instantiate a middleware stack
|
8
|
+
#
|
9
|
+
# @param application [Lotus::Application] the application
|
10
|
+
#
|
11
|
+
# @return [Lotus::Middleware] the new stack
|
12
|
+
#
|
13
|
+
# @since 0.1.0
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
# @see Lotus::Configuration
|
17
|
+
# @see http://rdoc.info/gems/rack/Rack/Builder
|
18
|
+
def initialize(application)
|
19
|
+
configuration = application.configuration
|
20
|
+
routes = application.routes
|
21
|
+
|
22
|
+
@builder = ::Rack::Builder.new
|
23
|
+
@builder.use Rack::Static,
|
24
|
+
urls: configuration.assets.entries,
|
25
|
+
root: configuration.assets
|
26
|
+
@builder.run routes
|
27
|
+
end
|
28
|
+
|
29
|
+
# Process a request.
|
30
|
+
# This method makes the middleware stack compatible with the Rack protocol.
|
31
|
+
#
|
32
|
+
# @param env [Hash] a Rack env
|
33
|
+
#
|
34
|
+
# @return [Array] a serialized Rack response
|
35
|
+
#
|
36
|
+
# @since 0.1.0
|
37
|
+
# @api private
|
38
|
+
def call(env)
|
39
|
+
@builder.call(env)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'lotus/utils/class'
|
2
|
+
require 'lotus/views/default'
|
3
|
+
require 'lotus/views/null_view'
|
4
|
+
|
5
|
+
module Lotus
|
6
|
+
# Rendering policy
|
7
|
+
#
|
8
|
+
# @since 0.1.0
|
9
|
+
# @api private
|
10
|
+
class RenderingPolicy
|
11
|
+
STATUS = 0
|
12
|
+
HEADERS = 1
|
13
|
+
BODY = 2
|
14
|
+
|
15
|
+
RACK_RESPONSE_SIZE = 3
|
16
|
+
|
17
|
+
SUCCESSFUL_STATUSES = (200..201).freeze
|
18
|
+
STATUSES_WITHOUT_BODY = Set.new((100..199).to_a << 204 << 205 << 301 << 302 << 304).freeze
|
19
|
+
RENDERABLE_FORMATS = [:all, :html].freeze
|
20
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
21
|
+
|
22
|
+
def initialize(configuration)
|
23
|
+
@controller_pattern = %r{#{ configuration.controller_pattern.gsub(/\%\{(controller|action)\}/) { "(?<#{ $1 }>(.*))" } }}
|
24
|
+
@view_pattern = configuration.view_pattern
|
25
|
+
@namespace = configuration.namespace
|
26
|
+
end
|
27
|
+
|
28
|
+
def render(response)
|
29
|
+
if renderable?(response)
|
30
|
+
action = response.pop
|
31
|
+
|
32
|
+
body = if successful?(response)
|
33
|
+
view_for(response, action).render(
|
34
|
+
action.to_rendering
|
35
|
+
)
|
36
|
+
else
|
37
|
+
if render_status_page?(response, action)
|
38
|
+
Lotus::Views::Default.render(response: response, format: :html)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
response[BODY] = Array(body) if body
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def renderable?(response)
|
48
|
+
response.size > RACK_RESPONSE_SIZE
|
49
|
+
end
|
50
|
+
|
51
|
+
def successful?(response)
|
52
|
+
SUCCESSFUL_STATUSES.include?(response[STATUS])
|
53
|
+
end
|
54
|
+
|
55
|
+
def render_status_page?(response, action)
|
56
|
+
RENDERABLE_FORMATS.include?(action.format) &&
|
57
|
+
!STATUSES_WITHOUT_BODY.include?(response[STATUS])
|
58
|
+
end
|
59
|
+
|
60
|
+
def view_for(response, action)
|
61
|
+
if response[BODY].empty?
|
62
|
+
captures = @controller_pattern.match(action.class.name)
|
63
|
+
Utils::Class.load!(@view_pattern % { controller: captures[:controller], action: captures[:action] }, @namespace)
|
64
|
+
else
|
65
|
+
Views::NullView.new(response[BODY])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/lotus/routes.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
module Lotus
|
2
|
+
# Routes factory
|
3
|
+
#
|
4
|
+
# A Lotus application has this factory instantiated by default and associated
|
5
|
+
# to the `Routes` constant, under the application namespace.
|
6
|
+
#
|
7
|
+
# @since 0.1.0
|
8
|
+
class Routes
|
9
|
+
# Initialize the factory
|
10
|
+
#
|
11
|
+
# @param routes [Lotus::Router] a routes set
|
12
|
+
#
|
13
|
+
# @return [Lotus::Routes] the factory
|
14
|
+
#
|
15
|
+
# @since 0.1.0
|
16
|
+
def initialize(routes)
|
17
|
+
@routes = routes
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return a relative path for the given route name
|
21
|
+
#
|
22
|
+
# @param name [Symbol] the route name
|
23
|
+
# @param args [Array,nil] an optional set of arguments that is passed down
|
24
|
+
# to the wrapped route set.
|
25
|
+
#
|
26
|
+
# @return [String] the corresponding relative URL
|
27
|
+
#
|
28
|
+
# @raise Lotus::Routing::InvalidRouteException
|
29
|
+
#
|
30
|
+
# @since 0.1.0
|
31
|
+
#
|
32
|
+
# @see http://rdoc.info/gems/lotus-router/Lotus/Router#path-instance_method
|
33
|
+
#
|
34
|
+
# @example
|
35
|
+
# require 'lotus'
|
36
|
+
#
|
37
|
+
# module Bookshelf
|
38
|
+
# class Application < Lotus::Application
|
39
|
+
# configure do
|
40
|
+
# routes do
|
41
|
+
# get '/login', to: 'sessions#new', as: :login
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# Bookshelf::Routes.path(:login)
|
48
|
+
# # => '/login'
|
49
|
+
#
|
50
|
+
# Bookshelf::Routes.path(:login, return_to: '/dashboard')
|
51
|
+
# # => '/login?return_to=%2Fdashboard'
|
52
|
+
def path(name, *args)
|
53
|
+
@routes.path(name, *args)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return an absolute path for the given route name
|
57
|
+
#
|
58
|
+
# @param name [Symbol] the route name
|
59
|
+
# @param args [Array,nil] an optional set of arguments that is passed down
|
60
|
+
# to the wrapped route set.
|
61
|
+
#
|
62
|
+
# @return [String] the corresponding absolute URL
|
63
|
+
#
|
64
|
+
# @raise Lotus::Routing::InvalidRouteException
|
65
|
+
#
|
66
|
+
# @since 0.1.0
|
67
|
+
#
|
68
|
+
# @see http://rdoc.info/gems/lotus-router/Lotus/Router#url-instance_method
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# require 'lotus'
|
72
|
+
#
|
73
|
+
# module Bookshelf
|
74
|
+
# class Application < Lotus::Application
|
75
|
+
# configure do
|
76
|
+
# routes do
|
77
|
+
# scheme 'https'
|
78
|
+
# host 'bookshelf.org'
|
79
|
+
#
|
80
|
+
# get '/login', to: 'sessions#new', as: :login
|
81
|
+
# end
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# Bookshelf::Routes.url(:login)
|
87
|
+
# # => 'https://bookshelf.org/login'
|
88
|
+
#
|
89
|
+
# Bookshelf::Routes.url(:login, return_to: '/dashboard')
|
90
|
+
# # => 'https://bookshelf.org/login?return_to=%2Fdashboard'
|
91
|
+
def url(name, *args)
|
92
|
+
@routes.url(name, *args)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Lotus
|
2
|
+
module Routing
|
3
|
+
# The default Rack application that responds when a resource cannot be found.
|
4
|
+
#
|
5
|
+
# @since 0.1.0
|
6
|
+
# @api private
|
7
|
+
class Default
|
8
|
+
DEFAULT_CODE = 404
|
9
|
+
DEFAULT_BODY = ['Not Found'].freeze
|
10
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
11
|
+
|
12
|
+
class NullAction
|
13
|
+
include Lotus::Action
|
14
|
+
|
15
|
+
def call(env)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
action = NullAction.new.tap {|a| a.call(env) }
|
21
|
+
[ DEFAULT_CODE, {CONTENT_TYPE => action.content_type}, DEFAULT_BODY, action ]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Lotus
|
2
|
+
module Views
|
3
|
+
# The default view that is rendered for non successful responses (200 and 201)
|
4
|
+
#
|
5
|
+
# @since 0.1.0
|
6
|
+
# @api private
|
7
|
+
class Default
|
8
|
+
include Lotus::View
|
9
|
+
configuration.reset!
|
10
|
+
|
11
|
+
layout nil
|
12
|
+
root Pathname.new(File.dirname(__FILE__)).join('../templates').realpath
|
13
|
+
template 'default'
|
14
|
+
|
15
|
+
def title
|
16
|
+
response[2].first
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/lotusrb.rb
CHANGED
data/lotusrb.gemspec
CHANGED
@@ -1,23 +1,30 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require '
|
4
|
+
require 'lotus/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
-
spec.version =
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.summary = %q{
|
12
|
-
spec.description = %q{
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
7
|
+
spec.name = 'lotusrb'
|
8
|
+
spec.version = Lotus::VERSION
|
9
|
+
spec.authors = ['Luca Guidi']
|
10
|
+
spec.email = ['me@lucaguidi.com']
|
11
|
+
spec.summary = %q{A complete web framework for Ruby}
|
12
|
+
spec.description = %q{A complete web framework for Ruby}
|
13
|
+
spec.homepage = 'http://lotusrb.org'
|
14
|
+
spec.license = 'MIT'
|
15
15
|
|
16
|
-
spec.files = `git ls-files`.split(
|
16
|
+
spec.files = `git ls-files -z -- lib/* LICENSE.md README.md lotusrb.gemspec`.split("\x0")
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test
|
19
|
-
spec.require_paths = [
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.
|
22
|
-
spec.
|
21
|
+
spec.add_dependency 'lotus-utils', '~> 0.2'
|
22
|
+
spec.add_dependency 'lotus-router', '~> 0.1', '>= 0.1.1'
|
23
|
+
spec.add_dependency 'lotus-controller', '~> 0.2'
|
24
|
+
spec.add_dependency 'lotus-view', '~> 0.2'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10'
|
28
|
+
spec.add_development_dependency 'minitest', '~> 5'
|
29
|
+
spec.add_development_dependency 'rack-test', '~> 0.6'
|
23
30
|
end
|