intranet-core 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +38 -0
- data/lib/core_extensions.rb +12 -0
- data/lib/core_extensions/string.rb +43 -0
- data/lib/core_extensions/tree.rb +84 -0
- data/lib/core_extensions/webrick/httpresponse.rb +22 -0
- data/lib/intranet/abstract_responder.rb +34 -0
- data/lib/intranet/core.rb +125 -0
- data/lib/intranet/core/builder.rb +98 -0
- data/lib/intranet/core/haml_wrapper.rb +60 -0
- data/lib/intranet/core/locales.rb +47 -0
- data/lib/intranet/core/servlet.rb +42 -0
- data/lib/intranet/core/version.rb +8 -0
- data/lib/intranet/logger.rb +38 -0
- data/lib/intranet/resources/haml/http_error.haml +27 -0
- data/lib/intranet/resources/haml/skeleton.haml +52 -0
- data/lib/intranet/resources/haml/title_and_breadcrumb.haml +8 -0
- data/lib/intranet/resources/locales/en.yml +46 -0
- data/lib/intranet/resources/locales/fr.yml +46 -0
- data/lib/intranet/resources/www/background.jpg +0 -0
- data/lib/intranet/resources/www/error.png +0 -0
- data/lib/intranet/resources/www/favicon.ico +0 -0
- data/lib/intranet/resources/www/fonts/SourceSansPro.woff2 +0 -0
- data/lib/intranet/resources/www/nav.js +25 -0
- data/lib/intranet/resources/www/style.css +306 -0
- data/spec/core_extensions/string_spec.rb +135 -0
- data/spec/core_extensions/tree_spec.rb +208 -0
- data/spec/core_extensions/webrick/httpresponse_spec.rb +43 -0
- data/spec/intranet/core/fr.yml +5 -0
- data/spec/intranet/core/haml_wrapper_spec.rb +70 -0
- data/spec/intranet/core/locales_spec.rb +74 -0
- data/spec/intranet/core_spec.rb +403 -0
- data/spec/intranet/logger_spec.rb +129 -0
- data/spec/spec_helper.rb +50 -0
- data/spec/test_responder/responder.rb +42 -0
- data/spec/test_responder/www/style.css +5 -0
- metadata +218 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e210409259e3ae615be11b386d4f59eba6857577
|
4
|
+
data.tar.gz: a971e1e9e6aac78e4ca5fc5503fc47682514a587
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 99566f62fd07cc59ba83f622469b433f2cfea9e099c0d6a14192e8ab00f2fb7857f31c1b18aff1b1a42cc35945334735c28164b868cb90b2d5d2e6175473a705
|
7
|
+
data.tar.gz: 239ded33d2cb26e300ae0b6d7d8b071d84d7d5e205ade2a10c0bf443f599f201aecfb5a6fa7ddaa8523f312782d0e7bc864770da0d51fe1db35bcf2bb922d435
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# intranet-core
|
2
|
+
|
3
|
+
`intranet-core` provides the core component of a generic, highly customizable
|
4
|
+
intranet built around [WEBrick HTTP server](https://rubygems.org/gems/webrick).
|
5
|
+
Each section of the intranet can be provided by a different _module_, which
|
6
|
+
is basically a Webrick servlet in charge of a specific subdirectory of the
|
7
|
+
web server.
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
### Creating a custom _module_
|
12
|
+
|
13
|
+
You can create a new _module_ by deriving from `Intranet::AbstractResponder`:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
require 'intranet/abstract_responder'
|
17
|
+
|
18
|
+
class MyModule << Intranet::AbstractResponder
|
19
|
+
def initialize(params = {})
|
20
|
+
@params = params
|
21
|
+
end
|
22
|
+
|
23
|
+
def generate_page(path, query)
|
24
|
+
# generate HTML for the given path
|
25
|
+
end
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
### Starting the Intranet
|
30
|
+
|
31
|
+
The Intranet is controlled by the `Intranet::Core` class.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
intranet = Intranet::Core.new
|
35
|
+
module = MyModule.new
|
36
|
+
intranet.register_module(module, 'my_module', __dir__)
|
37
|
+
intranet.start
|
38
|
+
```
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'core_extensions/string'
|
4
|
+
require_relative 'core_extensions/tree'
|
5
|
+
require_relative 'core_extensions/webrick/httpresponse'
|
6
|
+
|
7
|
+
String.include CoreExtensions::String
|
8
|
+
WEBrick::HTTPResponse.include CoreExtensions::WEBrick::HTTPResponse
|
9
|
+
|
10
|
+
# @!visibility protected
|
11
|
+
module CoreExtensions
|
12
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CoreExtensions
|
4
|
+
# @!visibility protected
|
5
|
+
# Extension of Ruby's standard library +String+ class.
|
6
|
+
module String
|
7
|
+
# Replaces all accented characters in a string with their non-accented version.
|
8
|
+
# @return [String]
|
9
|
+
def unaccentize # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
10
|
+
tr('ÀÁÂÃÄÅàáâãäåĀāĂ㥹', 'AAAAAAaaaaaaAaAaAa')
|
11
|
+
.tr('ÇçĆćĈĉĊċČčÐðĎďĐđ', 'CcCcCcCcCcDdDdDd')
|
12
|
+
.tr('ÈÉÊËèéêëĒēĔĕĖėĘęĚě', 'EEEEeeeeEeEeEeEeEe')
|
13
|
+
.tr('ĜĝĞğĠġĢģĤĥĦħ', 'GgGgGgGgHhHh')
|
14
|
+
.tr('ÌÍÎÏìíîïĨĩĪīĬĭĮįİı', 'IIIIiiiiIiIiIiIiIi')
|
15
|
+
.tr('ĴĵĶķĸĹĺĻļĽľĿŀŁł', 'JjKkkLlLlLlLlLl')
|
16
|
+
.tr('ÑñŃńŅņŇňʼnŊŋ', 'NnNnNnNnnNn')
|
17
|
+
.tr('ÒÓÔÕÖØòóôõöøŌōŎŏŐő', 'OOOOOOooooooOoOoOo')
|
18
|
+
.tr('ŔŕŖŗŘřŚśŜŝŞşŠšſŢţŤťŦŧ', 'RrRrRrSsSsSsSssTtTtTt')
|
19
|
+
.tr('ÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲų', 'UUUUuuuuUuUuUuUuUuUu')
|
20
|
+
.tr('ŴŵÝýÿŶŷŸŹźŻżŽž', 'WwYyyYyYZzZzZz')
|
21
|
+
.gsub(/ß/, 'ss')
|
22
|
+
.gsub(/Æ/, 'AE')
|
23
|
+
.gsub(/æ/, 'ae')
|
24
|
+
.gsub(/Œ/, 'OE')
|
25
|
+
.gsub(/œ/, 'oe')
|
26
|
+
.gsub(/IJ/, 'IJ')
|
27
|
+
.gsub(/ij/, 'ij')
|
28
|
+
end
|
29
|
+
|
30
|
+
# Converts a string to a snake-cased format suitable for URL and/or CSS attributes.
|
31
|
+
# @return [String]
|
32
|
+
def urlize
|
33
|
+
strip.unaccentize.downcase.tr(' \'', '_').delete('^-_a-z0-9')
|
34
|
+
end
|
35
|
+
|
36
|
+
# Tests whether a string is urlize-d, ie. it is only constituted of characters suitable for URL
|
37
|
+
# and/or CSS attributes.
|
38
|
+
# @return [Boolean]
|
39
|
+
def urlized?
|
40
|
+
scan(/[^-_a-z0-9]/).empty?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CoreExtensions
|
4
|
+
# N-ary tree data structure.
|
5
|
+
# Each node of the tree contains an +Object+ (the node value) and can be accessed from its parent
|
6
|
+
# node using an identifier +Object+. A node can be identified uniquely by the succession of
|
7
|
+
# identifiers that lead from the tree root to itself.
|
8
|
+
class Tree
|
9
|
+
# The value of the current tree node.
|
10
|
+
attr_accessor :value
|
11
|
+
|
12
|
+
# The child nodes of the current tree node.
|
13
|
+
# @return [Hash]
|
14
|
+
attr_reader :children_nodes
|
15
|
+
|
16
|
+
# Creates a new tree with a root element and no child nodes.
|
17
|
+
# @param node_value [Object] The value associated to the root element
|
18
|
+
def initialize(node_value = nil)
|
19
|
+
@value = node_value
|
20
|
+
@children_nodes = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Queries
|
24
|
+
|
25
|
+
# Check if the current Tree node has children.
|
26
|
+
def children?
|
27
|
+
!@children_nodes.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Tests whether the current tree node has a specific child.
|
31
|
+
# @param child_id [Object] The unique identifier of the child.
|
32
|
+
# @return [Boolean] True if the child node identified by +child_id+ exists, False otherwise.
|
33
|
+
def child_exists?(child_id)
|
34
|
+
child_node(child_id)
|
35
|
+
true
|
36
|
+
rescue KeyError
|
37
|
+
false
|
38
|
+
end
|
39
|
+
|
40
|
+
# Retrieves the child of the current tree node.
|
41
|
+
# @param child_id [Object] The unique identifier of the child.
|
42
|
+
# @return [Object] The node value of the child identified by +child_id+.
|
43
|
+
# @raise [KeyError] If the child node identified by +child_id+ does not exist.
|
44
|
+
def child_node(child_id)
|
45
|
+
@children_nodes.fetch(child_id)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a hash representation of the current tree node and all its children, recursively
|
49
|
+
# (depth-first).
|
50
|
+
# @param separator [String] The separator to be used between node identifiers.
|
51
|
+
# @return [Hash]
|
52
|
+
def to_h(separator = '/', id_prefix = '')
|
53
|
+
hash = {}
|
54
|
+
id_prefix.empty? ? hash[separator] = @value : hash[id_prefix] = @value
|
55
|
+
@children_nodes.each do |id, node|
|
56
|
+
hash.merge!(node.to_h(separator, id_prefix + separator + id.to_s))
|
57
|
+
end
|
58
|
+
hash
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the string representation of the current tree node and all its children, recursively
|
62
|
+
# (depth-first).
|
63
|
+
# @return [String]
|
64
|
+
def to_s(offset = '')
|
65
|
+
str = offset + 'VALUE: ' + @value.to_s + "\n"
|
66
|
+
@children_nodes.each do |id, node|
|
67
|
+
str += offset + ' * ID: \'' + id.to_s + '\'' + "\n"
|
68
|
+
str += node.to_s(offset + ' ')
|
69
|
+
end
|
70
|
+
str
|
71
|
+
end
|
72
|
+
|
73
|
+
# Retrieves the value associated to a child node of the current tree node, inserting it first if
|
74
|
+
# it does not exist.
|
75
|
+
# @param child_id [Object] The unique identifier for the child node to retrieve or insert.
|
76
|
+
# @param child_value [Object] The value associated to the child node to insert. Ignored if a
|
77
|
+
# child node identified by the given +child_id+ already exists.
|
78
|
+
# @return [Object] The value associated to the child node identified by the given +child_id+.
|
79
|
+
def add_child_node(child_id, child_value = nil)
|
80
|
+
@children_nodes[child_id] = Tree.new(child_value) if @children_nodes[child_id].nil?
|
81
|
+
@children_nodes[child_id]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'htmlentities'
|
4
|
+
require 'webrick'
|
5
|
+
require_relative '../../intranet/core/haml_wrapper'
|
6
|
+
|
7
|
+
module CoreExtensions
|
8
|
+
# @!visibility protected
|
9
|
+
module WEBrick
|
10
|
+
# @!visibility protected
|
11
|
+
# Extension of +WEBrick::HTTPResponse+ to provide the hook
|
12
|
+
# +create_error_page+.
|
13
|
+
module HTTPResponse
|
14
|
+
include Intranet::Core::HamlWrapper
|
15
|
+
|
16
|
+
# Provides custom error pages for common HTTP errors.
|
17
|
+
def create_error_page
|
18
|
+
@body << to_markup('http_error', error: @status)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Intranet
|
4
|
+
# The default implementation and interface of an Intranet module.
|
5
|
+
class AbstractResponder
|
6
|
+
# Destroys the responder instance.
|
7
|
+
# This method gets called when server is shut down.
|
8
|
+
def finalize
|
9
|
+
# nothing to do
|
10
|
+
end
|
11
|
+
|
12
|
+
# Generates the HTML content associated to the given +path+ and +query+.
|
13
|
+
# @param path [String] The requested URI, relative to that module root URI
|
14
|
+
# @param query [Hash] The URI variable/value pairs, if any
|
15
|
+
# @return [Array] The HTTP return code, the MIME type and the answer body.
|
16
|
+
def generate_page(path, query)
|
17
|
+
[404, '', '']
|
18
|
+
end
|
19
|
+
|
20
|
+
# Provides the list of Cascade Style Sheets (CSS) dependencies for this module.
|
21
|
+
# If redefined, this method should probably append dependencies rather than overwriting them.
|
22
|
+
# @return [Array] The list of CSS dependencies, as URL path from server root
|
23
|
+
def css_dependencies
|
24
|
+
['/design/style.css']
|
25
|
+
end
|
26
|
+
|
27
|
+
# Provides the list of Javascript files (JS) dependencies for this module.
|
28
|
+
# If redefined, this method should probably append dependencies rather than overwriting them.
|
29
|
+
# @return [Array] The list of JS dependencies, as URL path from server root
|
30
|
+
def js_dependencies
|
31
|
+
['/design/nav.js']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'webrick'
|
4
|
+
require_relative 'logger'
|
5
|
+
require_relative 'core/locales'
|
6
|
+
require_relative 'core/haml_wrapper'
|
7
|
+
require_relative 'core/builder'
|
8
|
+
require_relative 'core/servlet'
|
9
|
+
require_relative 'core/version'
|
10
|
+
require_relative '../core_extensions'
|
11
|
+
|
12
|
+
# The main Intranet namespace.
|
13
|
+
module Intranet
|
14
|
+
# The core of the Intranet.
|
15
|
+
class Core
|
16
|
+
# The port currently used by the Intranet.
|
17
|
+
# @return [Integer]
|
18
|
+
attr_reader :port
|
19
|
+
|
20
|
+
# Initializes a new Intranet core instance. The first available port will be used, starting at
|
21
|
+
# +preferred_port+. If +preferred_port+ port is 80 and the user has not enough priviledges to
|
22
|
+
# use that port, port 8080 (or one of the following ports) will be used.
|
23
|
+
# @param logger [Object] The logger.
|
24
|
+
# @param preferred_port [Integer] The preferred port for the web server.
|
25
|
+
def initialize(logger, preferred_port = 80)
|
26
|
+
@logger = logger
|
27
|
+
|
28
|
+
# Initialize translation module
|
29
|
+
Intranet::Core::Locales.initialize
|
30
|
+
|
31
|
+
# Initialize HAML wrapper
|
32
|
+
Intranet::Core::HamlWrapper.initialize
|
33
|
+
|
34
|
+
# Instanciate Intranet Builder and register page builders
|
35
|
+
@builder = Intranet::Core::Builder.new(@logger)
|
36
|
+
|
37
|
+
# Instanciate WebRick HTTP server
|
38
|
+
@server = load_http_server(preferred_port)
|
39
|
+
www_dir = File.join(__dir__, 'resources', 'www')
|
40
|
+
mount_default_servlets(www_dir)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Registers a new module to the core. The server must not be running. If +path+ is empty, the
|
44
|
+
# module will be used as Home module. Otherwise, the module will be accessible through the
|
45
|
+
# given +path+ under the web server root.
|
46
|
+
# @param responder [Intranet::AbstractResponder] The module responder instance.
|
47
|
+
# @param path [Array] The path, relative to the web server root, representing the module root
|
48
|
+
# directory. If empty, the responder will be registered as Home responder
|
49
|
+
# (to serve /index.html in particular). Subdirectories are allowed using
|
50
|
+
# an array element for each directory level. Each element must only contain
|
51
|
+
# the following characters: +-_a-z0-9+.
|
52
|
+
# @param resources_dir [String] The path to the directory that contains additional resources
|
53
|
+
# required by the module. This directory should contain three
|
54
|
+
# subdirectories: +haml/+, +locales/+ and +www/+.
|
55
|
+
# @raise [ArgumentError] If one of the element of the +path+ contains invalid characters.
|
56
|
+
# @raise [Errno::EALREADY] If the server is already running.
|
57
|
+
def register_module(responder, path, resources_dir)
|
58
|
+
raise ArgumentError unless path.all?(&:urlized?)
|
59
|
+
raise Errno::EALREADY if @server.status != :Stop
|
60
|
+
|
61
|
+
@builder.register(responder, path)
|
62
|
+
module_add_servlet(path.empty? ? ['home'] : path, resources_dir)
|
63
|
+
module_add_locales(resources_dir)
|
64
|
+
module_add_haml_templates(resources_dir)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Starts the web server.
|
68
|
+
def start
|
69
|
+
@logger.info('Intranet::Core: using locale \'' + I18n.default_locale.to_s + '\'')
|
70
|
+
@logger.info('Intranet::Core: running Intranet version ' + VERSION)
|
71
|
+
# Start serving HTTP requests
|
72
|
+
@server.start
|
73
|
+
end
|
74
|
+
|
75
|
+
# Stops the web server and finalizes all registered module responders.
|
76
|
+
def stop
|
77
|
+
@logger.info('Intranet::Runner: requesting system shutdown...')
|
78
|
+
@server.shutdown
|
79
|
+
@builder.finalize
|
80
|
+
@logger.close
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# See https://github.com/nahi/webrick/blob/master/lib/webrick/accesslog.rb#L69
|
86
|
+
ACCESSLOG_FMT = "%h '%r' -> %s (%b bytes in %Ts)"
|
87
|
+
|
88
|
+
def load_http_server(preferred_port)
|
89
|
+
@port = preferred_port
|
90
|
+
begin
|
91
|
+
WEBrick::HTTPServer.new(Port: @port, Logger: @logger, AccessLog: [[@logger, ACCESSLOG_FMT]])
|
92
|
+
rescue Errno::EACCES # not enough permission to use port 80
|
93
|
+
@port = 8080
|
94
|
+
retry
|
95
|
+
rescue Errno::EADDRINUSE
|
96
|
+
@port += 1
|
97
|
+
retry
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def mount_default_servlets(www_dir)
|
102
|
+
# Configure handlers for HTTP server
|
103
|
+
# Start serving www/ as HTTP server root "/" using the class WEBrick::HTTPServlet::FileHandler
|
104
|
+
# You can write your own servlet (it must inherit from WEBrick::HTTPServlet::AbstractServlet)
|
105
|
+
# http://ruby-doc.org/stdlib-1.9.3/libdoc/webrick/rdoc/WEBrick/HTTPServlet/AbstractServlet.html
|
106
|
+
# https://www.igvita.com/2007/02/13/building-dynamic-webrick-servers-in-ruby/
|
107
|
+
@server.mount('/design', WEBrick::HTTPServlet::FileHandler, www_dir)
|
108
|
+
@server.mount('/', Intranet::Core::Servlet, @builder)
|
109
|
+
end
|
110
|
+
|
111
|
+
def module_add_servlet(path, resources_dir)
|
112
|
+
@server.mount('/design/' + path.join('/'),
|
113
|
+
WEBrick::HTTPServlet::FileHandler,
|
114
|
+
File.join(resources_dir, 'www'))
|
115
|
+
end
|
116
|
+
|
117
|
+
def module_add_locales(resources_dir)
|
118
|
+
Intranet::Core::Locales.add_path(File.join(resources_dir, 'locales'))
|
119
|
+
end
|
120
|
+
|
121
|
+
def module_add_haml_templates(resources_dir)
|
122
|
+
Intranet::Core::HamlWrapper.add_path(File.join(resources_dir, 'haml'))
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'haml_wrapper'
|
4
|
+
require_relative 'version'
|
5
|
+
require_relative '../../core_extensions'
|
6
|
+
|
7
|
+
module Intranet
|
8
|
+
class Core
|
9
|
+
# @!visibility protected
|
10
|
+
# Builder for the Intranet. The builder is in charge of storing registered modules (responders
|
11
|
+
# instances) and to call the appropriate responder according to the received URL.
|
12
|
+
class Builder
|
13
|
+
include HamlWrapper
|
14
|
+
|
15
|
+
# The tree-like structure containing all registered responders (for Haml)
|
16
|
+
# @return [CoreExtensions::Tree]
|
17
|
+
attr_reader :responders
|
18
|
+
|
19
|
+
# Initializes a new builder.
|
20
|
+
# @param logger [Object] The logger.
|
21
|
+
def initialize(logger)
|
22
|
+
@logger = logger
|
23
|
+
@responders = CoreExtensions::Tree.new
|
24
|
+
end
|
25
|
+
|
26
|
+
# Finalizes the builder. Each registered responder is called for +finalize+.
|
27
|
+
def finalize
|
28
|
+
@responders.to_h.each do |path, responder|
|
29
|
+
next if responder.nil?
|
30
|
+
|
31
|
+
@logger.debug('Intranet::Builder: finalize responder at \'' + path + '\'')
|
32
|
+
responder.finalize
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Processes the given URL path and query. The corresponding responder is called to get the
|
37
|
+
# page content. If no responder can handle the request, HTTP error 404 is returned. If the
|
38
|
+
# responder returns a partial HTML content (HTTP error 206), it is assumed to be the page
|
39
|
+
# body and integrated into the Intranet template.
|
40
|
+
# @param path [String] The requested path, relative to the web server root. This path is
|
41
|
+
# supposed secured and normalized (no '../' in particular).
|
42
|
+
# @param query [Hash] The content of the GET parameters of the URL.
|
43
|
+
# @return [Array] The HTTP return code, the MIME type and the answer body.
|
44
|
+
def do_get(path, query = {})
|
45
|
+
resp, responder_path = get_responder(path)
|
46
|
+
status, mime_type, body = resp.generate_page(responder_path, query)
|
47
|
+
|
48
|
+
# Generate header and footer when partial content is returned by the responder
|
49
|
+
if status == 206 && mime_type == 'text/html'
|
50
|
+
body = to_markup('skeleton', is_home: path == '/index.html', body: body,
|
51
|
+
css: resp.css_dependencies, js: resp.js_dependencies)
|
52
|
+
status = 200
|
53
|
+
end
|
54
|
+
[status, mime_type, body]
|
55
|
+
rescue KeyError, NoMethodError
|
56
|
+
[404, '', '']
|
57
|
+
end
|
58
|
+
|
59
|
+
# Registers a new responder. If a responder is already registered with the same path, the new
|
60
|
+
# one overrides the old one.
|
61
|
+
# @param responder [Intranet::AbstractResponder] The responder instance of the module.
|
62
|
+
# @param path [Array] The path, relative to the web server root, representing the module root
|
63
|
+
# directory. If empty, the responder will be registered as Home responder
|
64
|
+
# (to serve /index.html in particular). Subdirectories are allowed using
|
65
|
+
# an array element for each directory level.
|
66
|
+
# @raise [ArgumentError] If one of the element of the +path+ contains invalid characters.
|
67
|
+
def register(responder, path = [])
|
68
|
+
raise ArgumentError unless path.all?(&:urlized?)
|
69
|
+
|
70
|
+
current_node = @responders
|
71
|
+
path.each do |part|
|
72
|
+
next if part.empty? || part == '.'
|
73
|
+
|
74
|
+
current_node = current_node.add_child_node(part)
|
75
|
+
end
|
76
|
+
current_node.value = responder
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# Get the responder instance associated with the given path.
|
82
|
+
# @param path [String] The absolute URL path.
|
83
|
+
# @return [Array] The responder instance (possibly nil) and the remaining of the URL path that
|
84
|
+
# has not been parsed.
|
85
|
+
def get_responder(path)
|
86
|
+
current_treenode = @responders
|
87
|
+
relative_path = path[1..-1].split('/').delete_if do |part|
|
88
|
+
if current_treenode.child_exists?(part)
|
89
|
+
current_treenode = current_treenode.child_node(part)
|
90
|
+
true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
[current_treenode.value, '/' + relative_path.join('/')]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|